C # Class Автоинкрементный ID - PullRequest
8 голосов
/ 13 февраля 2012

Я создаю класс в C # под названием «Робот», и каждому роботу требуется уникальное свойство идентификатора, которое дает им идентичность.

Есть ли способ создания автоинкрементного идентификатора для каждого нового объекта класса? Итак, если я создал 5 новых роботов, их идентификаторы соответственно будут 1, 2, 3, 4, 5. Если я затем уничтожу робота 2 и позже создам нового робота, у него будет идентификатор 2. И если я добавлю 6-й будет иметь идентификатор 6 и т. Д.

Спасибо.

Ответы [ 6 ]

28 голосов
/ 13 февраля 2012

Создайте статическую переменную экземпляра и используйте Interlocked.Increment(ref nextId) для нее.

class Robot {
    static int nextId;
    public int RobotId {get; private set;}
    Robot() {
        RobotId = Interlocked.Increment(ref nextId);
    }
}

Примечание # 1: использование nextId++ будет допустимо только в непараллельных средах; Interlocked.Increment работает, даже если вы выделяете своих роботов из нескольких потоков.

РЕДАКТИРОВАТЬ Это не касается повторного использования идентификаторов роботов. Если вам требуется повторное использование, решение будет намного более сложным: вам нужен список идентификаторов многократного использования и ReaderWriterLockSlim вокруг кода, который обращается к этому списку.

class Robot : IDisposable {
    static private int nextId;
    static private ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
    static private IList<int> reuseIds = new List<int>();
    public int RobotId {get; private set;}
    Robot() {
        rwLock.EnterReadLock();
        try {
            if (reuseIds.Count == 0) {
                RobotId = Interlocked.Increment(ref nextId);
                return;
            }
        } finally {
            rwLock.ExitReadLock();
        }
        rwLock.EnterWriteLock();
        try {
            // Check the count again, because we've released and re-obtained the lock
            if (reuseIds.Count != 0) {
                RobotId = reuseIds[0];
                reuseIds.RemoveAt(0);
                return;
            }
            RobotId = Interlocked.Increment(ref nextId);
        } finally {
            rwLock.ExitWriteLock();
        }
    }
    void Dispose() {
        rwLock.EnterWriteLock();
        reuseIds.Add(RobotId);
        rwLock.ExitWriteLock();
    }
}

Примечание # 2: Если вы хотите повторно использовать меньшие идентификаторы перед большими идентификаторами (в отличие от повторного использования идентификаторов, выпущенных ранее, до идентификаторов, выпущенных позже, как я их кодировал), вы можете заменить IList<int> на SortedSet<int> и внесите несколько корректировок в те части, где ID, который будет использоваться повторно, взят из коллекции.

11 голосов
/ 13 февраля 2012

Это сделает свое дело и будет работать с хорошим потокобезопасным способом.Конечно, вы сами можете утилизировать роботов и т. Д. Очевидно, что это не будет эффективно для большого количества роботов, но есть множество способов справиться с этим.

  public class Robot : IDisposable
  {
    private static List<bool> UsedCounter = new List<bool>();
    private static object Lock = new object();

    public int ID { get; private set; }

    public Robot()
    {

      lock (Lock)
      {
        int nextIndex = GetAvailableIndex();
        if (nextIndex == -1)
        {
          nextIndex = UsedCounter.Count;
          UsedCounter.Add(true);
        }

        ID = nextIndex;
      }
    }

    public void Dispose()
    {
      lock (Lock)
      {
        UsedCounter[ID] = false;
      }
    }


    private int GetAvailableIndex()
    {
      for (int i = 0; i < UsedCounter.Count; i++)
      {
        if (UsedCounter[i] == false)
        {
          return i;
        }
      }

      // Nothing available.
      return -1;
    }

Инекоторый тестовый код для хорошей меры.

[Test]
public void CanUseRobots()
{

  Robot robot1 = new Robot();
  Robot robot2 = new Robot();
  Robot robot3 = new Robot();

  Assert.AreEqual(0, robot1.ID);
  Assert.AreEqual(1, robot2.ID);
  Assert.AreEqual(2, robot3.ID);

  int expected = robot2.ID;
  robot2.Dispose();

  Robot robot4 = new Robot();
  Assert.AreEqual(expected, robot4.ID);
}
2 голосов
/ 13 февраля 2012

Нет такой встроенной функциональности.Вы должны реализовать это самостоятельно, например, удерживая массив битов, чтобы пометить используемые идентификаторы, а затем искать первый неиспользуемый идентификатор каждый раз, когда создаете нового робота.

Кстати, автоинкремент (всмысл базы данных) фактически означает, что вы продолжаете увеличивать счетчик, даже если одно или несколько ранее использованных значений больше не связаны с объектом.

Вот некоторый код:

public class Robot 
{
    private static const int MAX_ROBOTS = 100;
    private static bool[] usedIds = new bool[MAX_ROBOTS];
    public int Id { get; set; }

    public Robot()
    {
         this.Id = GetFirstUnused();             
    }

    private static int GetFirstUnused()
    {
         int foundId = -1;
         for(int i = 0; i < MAX_ROBOTS; i++)
         {
             if(usedIds[i] == false)
             {
                 foundId = usedIds[i];
                 usedIds[i] = true;
                 break;
             }
         }
         return foundId;
    }
}

ТамЕсть более сложные алгоритмы / структуры данных, чтобы найти первый неиспользованный менее чем за O (N), но это выходит за рамки моего поста.:)

2 голосов
/ 13 февраля 2012

Не совсем, однако вы можете использовать статический тип int, который вы инициализируете в классе и который увеличивается при вызове конструктора.

class Robot()
{
    static int nrOfInstances = 0;

    init _id;

    Robot()
    {
        _id = Robot.nrOfInstances;
        Robot.nrOfInstances++;
    }
}

(Надеюсь, синтаксис правильный, неткомпилятор здесь.)

Если вы хотите, чтобы удаленный идентификатор робота использовался повторно, не используйте счетчик, а используйте статический список и добавьте его в список.

Однако, чтоможет быть, лучше сохранить список используемых идентификаторов в другом классе, так что вам вообще не нужна статика.Всегда дважды подумайте, прежде чем использовать статический.Вы можете хранить список используемых идентификаторов в классе под названием «RobotCreator», «RobotHandler», «RobotFactory» (не так, как шаблон проектирования).

1 голос
/ 13 февраля 2012
class Robot : IDisposable
{
    static private int IdNext = 0;
    static private int IdOfDestroy = -1;

    public int RobotID
    {
        get;
        private set;
    }

    public Robot()
    {
        if(IdOfDestroy == -1)
        {
            this.RobotID = Robot.IdNext;
            Robot.IdNext++;

        }
        else
        {
            this.RobotID = Robot.IdOfDestroy;
        }
    }

    public void Dispose()
    {
        Robot.IdOfDestroy = this.RobotID;
    }
}

Я надеюсь, что может помочь вам 100

0 голосов
/ 05 июня 2017
public static void beAddedTo<T>(this T item, Dictionary<int, T> dic) where T : m.lib.RandId
{
    Random ran = new Random();
    var ri = ran.Next();
    while (Program.DB.Rooms.ContainsKey(ri)) ri = ran.Next();
    item.Id = ri;
    dic.Add(item.Id, item);
}

Не инкрементно, но вы можете добавлять и удалять элементы сколько угодно раз.(Максимальный элемент должен быть ниже, чем int.Max / 2)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...