C#类自动增量ID

Ste*_*unn 8 c# identity class member auto-increment

我在C#中创建了一个名为"Robot"的类,每个机器人都需要一个唯一的ID属性,它给自己一个身份.

有没有办法为每个新的类对象创建一个自动增量ID?因此,如果我创建了5个新机器人,它们的ID将分别为1,2,3,4,5.如果我随后销毁机器人2并在以后创建新机器人,则其ID为2.如果我添加了6,它的ID为6,依旧等等.

谢谢.

das*_*ght 28

创建一个静态实例变量,并Interlocked.Increment(ref nextId)在其上使用.

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

注意#1:using nextId++仅在非并发环境中有效; Interlocked.Increment即使您从多个线程分配机器人也能正常工作.

编辑这不涉及重复使用机器人ID.如果需要重用,解决方案要复杂得多:您需要一个可重用ID列表,以及ReaderWriterLockSlim访问该列表的代码周围.

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

注2:如果你想提前重用较小的ID较大的ID(而不是重用晚些时候公布的ID之前早些时候发布的ID,我编码它),你可以替换IList<int>使用SortedSet<int>,使零件周围一些调整,其中一个ID被重用是从集合中获取的.

  • 哇靠!我无法相信这是解决明显竞争条件的唯一答案. (4认同)
  • @Tudor:竞争条件是编程的基本概念,特别是在.NET世界中. (3认同)
  • @Tudor:在这个时代,我们真的没有乐趣假设一个单线程环境。 (2认同)

A.R*_*.R. 11

这将解决问题,并以一种良好的线程安全方式运行.当然,由你自己处理机器人等等.显然,对于大量的机器人来说它不会有效,但有很多方法可以解决这个问题.

  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;
    }
Run Code Online (Sandbox Code Playgroud)

还有一些测试代码可以很好地衡量.

[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);
}
Run Code Online (Sandbox Code Playgroud)