可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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);
}
回答2:
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.
回答3:
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).
回答4:
There is no such built-in functionality. You have to implement it yourself, like holding an array of bits to mark the used ids and then searching for the first unused id every time you create a new robot.
By the way, auto-increment (in a database sense) actually means that you keep incrementing the counter even if one or more of the previously used values are no longer associated to an object.
Here is some code:
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;
}
}
There are more sophisticated algorithms / data structures to find the first unused in less than O(N), but this is beyond the scope of my post. :)
回答5:
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;
}
}
I hope can help you !
回答6:
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);
}
Not incremental but you can add and delete item how many time you want.
(Maximum item should be lower than int.Max/2)