2012-02-13 11 views
5

Sto creando una classe in C# denominata "Robot", e ogni robot richiede una proprietà ID univoca che si dà un'identità.C# Class Auto incremento ID

Esiste un modo per creare un ID incrementale automatico per ogni nuovo oggetto di classe? Quindi, se ho creato 5 nuovi robot, i loro ID rispettivamente saranno 1, 2, 3, 4, 5. Se poi distruggo il robot 2 e creerò un nuovo robot in un secondo momento, avrò l'ID di 2. E se aggiungo un 6 avrà l'ID di 6 e così via ..

Grazie.

+9

"Se poi distruggo il robot 2 e creerò un nuovo robot in un secondo momento, avrà l'ID di 2." Questo non suona come il concetto base di auto-incremento per me. – BoltClock

+0

Le istanze del robot sono persistenti in alcuni archivi dati? SQL Server, Access, ecc. – Bryan

risposta

5

Questo farà il trucco e funzionerà in un modo sicuro e sicuro. Naturalmente spetta a te smaltire autonomamente i robot, ecc.Ovviamente non sarà efficiente per un gran numero di robot, ma ci sono molti modi per affrontarlo.

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

E qualche codice di prova per buona misura.

[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

Questo era eccellente! – rajcool111

2

Non proprio, tuttavia è possibile utilizzare un int statico che si inizializza nella classe e viene incrementato quando viene chiamato il costruttore.

class Robot() 
{ 
    static int nrOfInstances = 0; 

    init _id; 

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

(spero che la sintassi è giusto, non hanno un compilatore qui.)

Se si vuole avere un ID robot rimosso essere riutilizzato, non utilizzare un contatore, ma utilizzare un lista statica e aggiungerla alla lista.

Tuttavia, ciò che potrebbe essere migliore è mantenere l'elenco degli ID utilizzati in un'altra classe, in modo da non avere affatto bisogno della statica. Pensaci sempre prima di usare una statica. È possibile mantenere l'elenco degli ID utilizzati in una classe denominata 'RobotCreator', 'RobotHandler', 'RobotFactory' (non come il modello di progettazione).

24

Creare una variabile di istanza statica e utilizzare Interlocked.Increment(ref nextId) su di esso.

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

Nota # 1: usando nextId++ sarebbe valida solo in ambienti non concorrenti; Interlocked.Increment funziona anche se si assegnano i robot da più thread.

EDIT Questo non riguarda il riutilizzo di ID robot. Se è necessario riutilizzare, la soluzione è molto più complessa: è necessario un elenco di ID riutilizzabili e un codice ReaderWriterLockSlim relativo al codice che accede a tale elenco.

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: Se volete riutilizzare gli ID più piccoli davanti di ID più grandi (al contrario di riutilizzare gli ID rilasciati in precedenza prima di IDs rilasciati più tardi, come ho codificato esso) è possibile sostituire IList<int> con SortedSet<int> e fare una alcuni aggiustamenti intorno alle parti in cui un ID da riutilizzare viene prelevato dalla collezione.

+1

Un incremento classico è sufficiente in un ambiente a thread singolo. – Tudor

+3

santo schifo! Non posso credere che questa sia l'unica risposta che risolve l'ovvia condizione di gara. –

+1

@Tudor: Al giorno d'oggi non abbiamo il piacere di assumere un singolo ambiente thread. –

2

Nessuna funzionalità incorporata. Devi implementarlo da solo, come tenere una serie di bit per contrassegnare gli ID utilizzati e quindi cercare il primo id non utilizzato ogni volta che crei un nuovo robot.

A proposito, l'incremento automatico (in un rilevamento del database) significa in realtà che si continua ad incrementare il contatore anche se uno o più valori precedentemente utilizzati non sono più associati a un oggetto.

Ecco il codice:

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

Ci sono più sofisticati algoritmi/strutture di dati per trovare la prima inutilizzata in meno di O (N), ma questo è oltre la portata del mio post. :)

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

spero può aiutare!

+0

Questo non funzionerà come previsto. Supponiamo di avere 3 robot, inizialmente con ID 1, 2, 3. Se li dispongo tutti in questo ordine, l'ultimo distrutto sarà no. 3, quindi il prossimo robot che creerò avrà ID 3, non 1 come previsto. In effetti, 'IdOfDestroy' rimarrà 3, quindi il prossimo robot creato avrà anche l'id 3. – Tudor

+0

sì @Tudor, hai ragione, mi dispiace che il mio codice non funzioni, ti ringrazio molto. –

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

Non incrementale ma si possono aggiungere ed eliminare voce quante volte si desidera. (L'articolo massimo deve essere inferiore a int.Max/2)