C# Class Auto increment ID
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.
This will do the trick, and operate in a nice threadsafe way. Of course it is up to you to dispose the robots yourself, etc. Obviously it won't be efficient for a large number of robots, but there are tons of ways to deal with that.
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;
}
And some test code for good measure.
[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);
}
Not really, however you can use a static int which you initialize in the class and is incremented when the constructor is called.
class Robot()
{
static int nrOfInstances = 0;
init _id;
Robot()
{
_id = Robot.nrOfInstances;
Robot.nrOfInstances++;
}
}
(I hope the syntax is right, don't have a compiler here.)
If you want to have a removed robot ID being reused, don't use a counter, but use a static list and add it to the list.
However, what might be better is to keep the list of used IDs in another class, so you don't need the static at all. Always think twice before you use a static. You might keep the list of used IDs in a class called 'RobotCreator', 'RobotHandler', 'RobotFactory' (not like the design pattern).