web-dev-qa-db-ja.com

C#クラス自動インクリメントID

私は「ロボット」と呼ばれるC#でクラスを作成していますが、各ロボットには、自分自身にIDを与える一意のIDプロパティが必要です。

新しいクラスオブジェクトごとに自動インクリメンタルIDを作成する方法はありますか?したがって、5つの新しいロボットを作成した場合、それらのIDはそれぞれ1、2、3、4、5になります。次にロボット2を破棄して後で新しいロボットを作成すると、IDは2になります。 6番目のIDは6になります。

ありがとう。

9
Stefan Dunn

これでうまくいき、スレッドセーフな方法で動作します。もちろん、ロボットを自分で処分するなどはあなた次第です。明らかに、多数のロボットにとっては効率的ではありませんが、それに対処する方法はたくさんあります。

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

そして、適切な測定のためのいくつかのテストコード。

[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);
}
11
A.R.

静的インスタンス変数を作成し、それにInterlocked.Increment(ref nextId)を使用します。

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

注1:nextId++の使用は、非並行環境でのみ有効です。 Interlocked.Incrementは、ロボットを複数のスレッドから割り当てた場合でも機能します。

[〜#〜] edit [〜#〜]これはロボット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();
    }
}

注2:大きいIDの前に小さいIDを再利用したい場合(私がコーディングしたように、後でリリースされるIDの前にリリースされたIDを再利用するのではなく)、IList<int>SortedSet<int> そして、再利用されるIDがコレクションから取得される部分の周りでいくつかの調整を行います。

31
dasblinkenlight

ただし、実際にはそうではありませんが、クラスで初期化し、コンストラクターが呼び出されたときにインクリメントされる静的intを使用できます。

class Robot()
{
    static int nrOfInstances = 0;

    init _id;

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

(構文が正しいことを願っています。ここにコンパイラーはありません。)

削除したロボットIDを再利用したい場合は、カウンターを使用せず、静的リストを使用してリストに追加してください。

ただし、使用されているIDのリストを別のクラスに保持する方がよい場合があるため、静的なものはまったく必要ありません。スタティックを使用する前に、常によく考えてください。使用されているIDのリストは、「RobotCreator」、「RobotHandler」、「RobotFactory」というクラスに保持できます(デザインパターンとは異なります)。

2
Michel Keijzers

そのような組み込み機能はありません。使用済みIDをマークするビット配列を保持し、新しいロボットを作成するたびに最初の未使用IDを検索するなど、自分で実装する必要があります。

ちなみに、(データベースの意味での)自動インクリメントとは、実際には、以前に使用された1つ以上の値がオブジェクトに関連付けられなくなった場合でも、カウンターをインクリメントし続けることを意味します。

ここにいくつかのコードがあります:

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

O(N)未満で最初の未使用を見つけるためのより洗練されたアルゴリズム/データ構造がありますが、これは私の投稿の範囲を超えています。 :)

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

お役に立てば幸いです!

1
Ruiqiang Liu
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);
}

増分ではありませんが、アイテムを何度でも追加および削除できます。 (最大項目はint.Max/2より低くする必要があります)

0
Kaiba Zax