2012-02-13 31 views
5

Tôi đang tạo một lớp trong C# gọi là "Robot" và mỗi rô-bốt yêu cầu một thuộc tính ID duy nhất cung cấp cho bản sắc.C# Class Auto increment ID

Có cách nào để tạo ID gia tăng tự động cho từng đối tượng lớp mới không? Vì vậy, nếu tôi tạo 5 rô bốt mới, ID của chúng tương ứng sẽ là 1, 2, 3, 4, 5. Nếu sau đó tôi tiêu diệt rô bốt 2 và tạo rô bốt mới sau, nó sẽ có ID là 2. Và nếu tôi thêm Thứ 6 nó sẽ có ID của 6 và như vậy ..

Cảm ơn.

+9

"Nếu sau đó tôi tiêu diệt rô bốt 2 và tạo rô bốt mới sau, nó sẽ có ID là 2." Điều đó không có vẻ giống như khái niệm cơ bản về tăng tự động đối với tôi. – BoltClock

+0

Các phiên bản robot có tồn tại trong một số kho dữ liệu không? SQL Server, Access, v.v. – Bryan

Trả lời

5

Điều này sẽ thực hiện thủ thuật và vận hành theo cách an toàn đẹp mắt. Tất nhiên, bạn có thể tự mình bỏ các rô bốt, v.v.Rõ ràng nó sẽ không hiệu quả đối với một số lượng lớn các rô bốt, nhưng có rất nhiều cách để đối phó với điều đó.

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

Và một số mã thử nghiệm để có biện pháp tốt.

[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

Điều này thật tuyệt vời! – rajcool111

2

Không thực sự, tuy nhiên bạn có thể sử dụng một int tĩnh mà bạn khởi tạo trong lớp và được tăng lên khi hàm tạo được gọi.

class Robot() 
{ 
    static int nrOfInstances = 0; 

    init _id; 

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

(Tôi hy vọng cú pháp là đúng, không có một trình biên dịch ở đây.)

Nếu bạn muốn có một ID Robot loại bỏ được tái sử dụng, không sử dụng một bộ đếm, nhưng sử dụng một danh sách tĩnh và thêm nó vào danh sách.

Tuy nhiên, điều tốt hơn là giữ danh sách ID được sử dụng trong một lớp khác, vì vậy bạn không cần tĩnh. Luôn suy nghĩ kỹ trước khi sử dụng tĩnh. Bạn có thể giữ danh sách các ID được sử dụng trong một lớp được gọi là 'RobotCreator', 'RobotHandler', 'RobotFactory' (không giống như mẫu thiết kế).

24

Tạo biến mẫu tĩnh và sử dụng Interlocked.Increment(ref nextId) trên đó.

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

Lưu ý # 1: sử dụng nextId++ sẽ chỉ hợp lệ trong môi trường không đồng thời; Interlocked.Increment hoạt động ngay cả khi bạn phân bổ rô bốt của mình từ nhiều chuỗi.

EDIT Điều này không giải quyết việc sử dụng lại ID robot. Nếu bạn cần sử dụng lại, giải pháp phức tạp hơn rất nhiều: bạn cần một danh sách các ID có thể sử dụng lại và ReaderWriterLockSlim xung quanh mã truy cập danh sách đó.

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

Lưu ý # 2: Nếu bạn muốn sử dụng lại ID nhỏ hơn trước ID lớn hơn (như trái ngược với cách sử dụng lại ID phát hành trước đó trước khi ID phát hành sau, khi tôi mã hoá nó), bạn có thể thay thế IList<int> với SortedSet<int> và thực hiện một vài điều chỉnh xung quanh các phần mà một ID được sử dụng lại được lấy từ bộ sưu tập.

+1

Giá trị gia tăng cổ điển là đủ trong môi trường đơn luồng. – Tudor

+3

holy crap! Tôi không thể tin rằng đây là câu trả lời duy nhất giải quyết tình trạng cuộc đua rõ ràng. –

+1

@Tudor: Trong ngày và tuổi tác, chúng tôi không thực sự có niềm vui của giả định một môi trường luồng đơn. –

2

Không có chức năng tích hợp sẵn như vậy. Bạn phải tự mình thực hiện, như giữ một mảng bit để đánh dấu các id đã sử dụng và sau đó tìm kiếm id không sử dụng đầu tiên mỗi lần bạn tạo một rô bốt mới.

Bằng cách này, tự động tăng (theo nghĩa cơ sở dữ liệu) thực sự có nghĩa là bạn tiếp tục tăng bộ đếm ngay cả khi một hoặc nhiều giá trị đã sử dụng trước đó không còn được liên kết với đối tượng.

Dưới đây là một số mã:

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

Có những thuật toán phức tạp hơn/cấu trúc dữ liệu để tìm ra chưa sử dụng lần đầu tiên trong vòng chưa đầy O (N), nhưng điều này nằm ngoài phạm vi của bài viết của tôi. :)

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

Tôi hy vọng có thể giúp bạn!

+0

Điều này sẽ không hoạt động như mong đợi. Giả sử tôi có 3 robot, ban đầu với id 1, 2, 3. Nếu tôi vứt bỏ tất cả chúng theo thứ tự này, robot bị hủy cuối cùng sẽ không có. 3, vì vậy robot tiếp theo tôi tạo sẽ có id 3, không phải 1 như mong đợi. Trong thực tế, 'IdOfDestroy' sẽ vẫn còn 3, do đó, robot được tạo tiếp theo cũng sẽ có id 3. – Tudor

+0

yes @Tudor, bạn nói đúng, tôi xin lỗi mã của tôi sẽ không hoạt động như mong đợi , cảm ơn bạn rất nhiều. –

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

Không cộng dồn nhưng bạn có thể thêm và xóa mục có bao nhiêu thời gian bạn muốn. (Mục tối đa phải nhỏ hơn int.Max/2)

Các vấn đề liên quan