2012-02-13 5 views
5

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

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

Спасибо.

+9

«Если я затем уничтожу робота 2 и создаю новый робот позже, он будет иметь идентификатор 2.» Это не похоже на базовую концепцию автоинкремента для меня. – BoltClock

+0

Являются ли экземпляры роботов постоянными в каком-то хранилище данных? SQL Server, Access и т. Д. – Bryan

ответ

5

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

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); 
} 
+0

Это было отлично! – rajcool111

2

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

class Robot() 
{ 
    static int nrOfInstances = 0; 

    init _id; 

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

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

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

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

24

Создайте переменную статического экземпляра и используйте на нем 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 работает, даже если вы выделяете своих роботов из нескольких потоков.

EDIT Это не касается повторного использования идентификаторов роботов. Если вам нужно повторное использование, решение намного сложнее: вам нужен список многоразовых идентификаторов и 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> и сделать несколько корректировок вокруг частей, где идентификатор, который будет использоваться повторно, берется из коллекции.

+1

В однопоточной среде достаточно классического прироста. – Tudor

+3

святое дерьмо! Я не могу поверить, что это единственный ответ, который касается очевидного состояния гонки. –

+1

@Tudor: В наши дни мы не очень довольны тем, что принимаем однопоточную среду. –

2

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

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

Вот код:

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), но это выходит за рамки моего поста. :)

1
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; 
    } 
} 

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

+0

Это не будет работать должным образом. Предположим, что у меня есть 3 робота, изначально с идентификаторами 1, 2, 3. Если я уничтожу все из них в этом порядке, последний уничтоженный будет отсутствовать. 3, поэтому следующий робот, который я создаю, будет иметь id 3, а не 1, как ожидалось. Фактически, 'IdOfDestroy' останется 3, поэтому следующий созданный робот также будет иметь id 3. – Tudor

+0

yes @Tudor, вы правы, мне жаль, что мой код не будет работать, спасибо вам большое. –

0
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)