2012-02-13 47 views
5

Estoy creando una clase en C# llamada "Robot", y cada robot requiere una propiedad de ID única que le otorgue una identidad.C# Class Incremento automático ID

¿Hay alguna manera de crear una identificación incremental automática para cada nuevo objeto de clase? Entonces, si creé 5 nuevos robots, sus IDs respectivamente serán 1, 2, 3, 4, 5. Si luego destruyo el robot 2 y creo un nuevo robot más adelante, tendrá la ID de 2. Y si agrego un 6to tendrá la identificación de 6 y así sucesivamente ..

Gracias.

+9

"Si luego destruyo el robot 2 y creo un nuevo robot más adelante, tendrá el ID de 2." Eso no suena como el concepto básico de autoincremento para mí. – BoltClock

+0

¿Las instancias del robot persisten en algún almacén de datos? SQL Server, Access, etc. – Bryan

Respuesta

5

Esto hará el truco, y funcionar de una manera segura y segura. Por supuesto, depende de usted desechar los robots usted mismo, etc.Obviamente, no será eficiente para una gran cantidad de robots, pero hay muchas maneras de lidiar con eso.

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

Y algunos códigos de prueba para una buena medida.

[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

¡Esto fue excelente! – rajcool111

2

No obstante, en realidad no se puede usar una int estática que se inicializa en la clase y se incrementa cuando se llama al constructor.

class Robot() 
{ 
    static int nrOfInstances = 0; 

    init _id; 

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

(espero que la sintaxis es correcta, no tiene un compilador aquí.)

Si usted quiere tener un ID de robot eliminado ser reutilizado, no utilice un contador, pero el uso de una lista estática y agréguela a la lista.

Sin embargo, lo que podría ser mejor es mantener la lista de ID usados ​​en otra clase, por lo que no necesita la estática en absoluto. Siempre piense dos veces antes de usar una estática. Puede mantener la lista de ID usados ​​en una clase llamada 'RobotCreator', 'RobotHandler', 'RobotFactory' (no como el patrón de diseño).

24

Crea una variable de instancia estática y usa Interlocked.Increment(ref nextId) en ella.

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

Note # 1: usando nextId++ sería válida sólo en entornos no concurrentes; Interlocked.Increment funciona incluso si asigna sus robots desde múltiples hilos.

EDIT Esto no se ocupa de la reutilización de identificadores de robot. Si necesita reutilizar, la solución es mucho más compleja: necesita una lista de ID reutilizables y un ReaderWriterLockSlim alrededor del código que accede a esa lista.

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(); 
    } 
} 

Nota # 2: Si desea volver a utilizar los identificadores más pequeños por delante de los identificadores de mayor tamaño (en contraposición a la reutilización de identificadores liberados antes antes de identificadores liberados más tarde, como codifiqué ella) puede reemplazar IList<int> con SortedSet<int> y hacer una algunos ajustes alrededor de las partes donde una identificación para ser reutilizada se toma de la colección.

+1

Un incremento clásico es suficiente en un entorno de subproceso único. – Tudor

+3

¡Santa mierda! No puedo creer que esta es la única respuesta que aborda la condición de carrera obvia. –

+1

@Tudor: En este día y edad, realmente no tenemos el placer de asumir un solo entorno de rosca. –

2

No hay tal funcionalidad incorporada. Tienes que implementarlo tú mismo, como mantener un conjunto de bits para marcar los identificadores usados ​​y luego buscar el primer ID no utilizado cada vez que creas un nuevo robot.

Por cierto, el auto-incremento (en el sentido de la base de datos) en realidad significa que se sigue incrementando el contador incluso si uno o más de los valores utilizados anteriormente ya no están asociados a un objeto.

Aquí hay un código:

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

Hay más sofisticados algoritmos/estructuras de datos para encontrar el primero no utilizada en menos de O (N), pero esto está fuera del alcance de mi puesto. :)

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

espero que le puede ayudar!

+0

Esto no funcionará como se esperaba. Supongamos que tengo 3 robots, inicialmente con los identificadores 1, 2 y 3. Si elimino todos ellos en este orden, el último destruido será no. 3, entonces el próximo robot que creé tendrá id 3, no 1 como se esperaba. De hecho, 'IdOfDestroy' seguirá siendo 3, por lo que el próximo robot creado también tendrá id 3. – Tudor

+0

sí @Tudor, tienes razón, lo siento, mi código no funcionará esperado, muchas gracias. –

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); 
} 
No

incrementales pero se puede añadir y eliminar el elemento cuántas momento que desee. (El artículo máximo debe ser inferior a int.Max/2)

Cuestiones relacionadas