2012-02-13 20 views
5

私はC#で "ロボット"と呼ばれるクラスを作成していますが、各ロボットには独自のIDプロパティが必要です。C#クラス自動インクリメントID

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

ありがとうございました。

+9

「ロボット2を破棄して後で新しいロボットを作成すると、IDは2になります。それは私の自動インクリメントの基本コンセプトのようには聞こえません。 – BoltClock

+0

ロボットインスタンスが一部のデータストアに永続化されていますか? SQL Server、Accessなど – Bryan

答えて

5

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

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); 
} 
+0

これは素晴らしかった! – rajcool111

2

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

class Robot() 
{ 
    static int nrOfInstances = 0; 

    init _id; 

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

あなたが再利用されて削除されたロボットのIDを持つようにしたい場合は、カウンタを使用していない

(私は。ここにコンパイラを持っていない、構文が正しいことを願っ)が、使用静的リストを作成し、リストに追加します。

ただし、使用したIDのリストを別のクラスに保存しておくと、静的なものはまったく必要ありません。あなたは静的を使用する前に、常に二度考えます。使用したIDのリストを 'RobotCreator'、 'RobotHandler'、 'RobotFactory'というクラスに保存することもできます(デザインパターンとは異なります)。

24

静的インスタンス変数を作成し、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を再利用したい場合、あなたはSortedSet<int>IList<int>を交換し、作ることができます再利用されるIDがコレクションから取得される部分の周りの調整はほとんどありません。

+1

シングルスレッド環境では古典的な増分で十分です。 – Tudor

+3

聖なるくそ!明らかな競合状態に対処する唯一の答えだとは思えません。 –

+1

@ Tudor:この日、私たちは本当に単一のスレッド環境を想定する喜びはありません。 –

2

このような組み込み機能はありません。使用されている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)未満で最初の未使用のを見つけるために、より洗練されたアルゴリズム/データ構造があるが、これは私の記事の範囲を超えています。 :)

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

私は、増分

+0

これは期待どおりに動作しません。私は3つのロボットを持っていると仮定します。最初は1,2,3のIDを持ちます。これらのすべてをこの順序で処分すれば、最後に破壊されたものはノーとなります。 3、私が作成する次のロボットは期待通りに1ではなくid 3を持つでしょう。実際には、 'IdOfDestroy'は3のままなので、次に作成されるロボットはID3を持ちます。 – Tudor

+0

はい@Tudor、あなたは正しいです、私のコードは期待通りに動作しません。 –

0
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より小さくする必要があります)

関連する問題