C# Class Auto increment ID

Stefan Dunn picture Stefan Dunn · Feb 13, 2012 · Viewed 53.9k times · Source

I am creating a class in C# called "Robot", and each robot requires a unique ID property which gives themselves an identity.

Is there any way of creating an auto incremental ID for each new class object? So, If i created 5 new robots, their IDs respectively will be 1, 2, 3, 4, 5. If I then destroy robot 2 and create a new robot later, it will have the ID of 2. And if I add a 6th it will have the ID of 6 and so on..

Thanks.

Answer

Sergey Kalinichenko picture Sergey Kalinichenko · Feb 13, 2012

Create a static instance variable, and use Interlocked.Increment(ref nextId) on it.

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

Note #1: using nextId++ would be valid only in non-concurrent environments; Interlocked.Increment works even if you allocate your robots from multiple threads.

EDIT This does not deal with re-using robot IDs. If you need reuse, the solution is a lot more complex: you need a list of reusable IDs, and a ReaderWriterLockSlim around the code that accesses that list.

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

Note #2: If you would like to reuse smaller IDs ahead of larger IDs (as opposed to reusing IDs released earlier before IDs released later, as I coded it) you can replace IList<int> with SortedSet<int> and make a few adjustments around the parts where an ID to be reused is taken from the collection.