在实例化时提供类唯一ID:.Net

时间:2008-09-15 15:34:23

标签: .net vb.net

每次实例化一个新的时候,我想给一个类一个唯一的ID。例如,对于名为Foo的类,我希望能够执行以下操作

dim a as New Foo()
dim b as New Foo()

并且a将获得唯一ID并且b将获得唯一ID。 id只需要在运行时是唯一的,所以我只想使用整数。我已经找到了一种方法来做到这一点(并且这是警告)我不希望能够从任何地方更改ID。我目前关于实现这一目标的方法的想法如下:

Public Class test
    Private Shared ReadOnly _nextId As Integer
    Private ReadOnly _id As Integer
    Public Sub New()
        _nextId = _nextId + 1
        _id = _nextId
    End Sub
End Class

然而,这将无法编译,因为它会引发错误 _nextId = _nextId + 1 我不明白为什么这会是一个错误(因为_Id也是readonly你应该能够改变构造函数中的只读变量。)我认为这也与它共享它有关。任何解决方案(希望不是kludgy hehe)或解释为什么这不起作用将被接受。重要的部分是我想要两个变量(或者如果有一种方法只有一个甚至更好但我认为不可能)在初始化对象后是不可变的。谢谢!

9 个答案:

答案 0 :(得分:5)

此设计容易受到多线程问题的影响。我强烈建议您使用Guids作为您的ID(Guid.NewGuid())。如果您绝对必须使用整数,请查看Interlocked课程。您可以将所有递增和Id逻辑包装在基类中,这样您只能在一个位置访问ID生成器。

答案 1 :(得分:2)

请考虑以下代码:

Public Class Foo 
    Private ReadOnly _fooId As FooId 

    Public Sub New() 
        _fooId = New FooId() 
    End Sub 

    Public ReadOnly Property Id() As Integer 
        Get 
            Return _fooId.Id 
        End Get 
    End Property 
End Class 

Public NotInheritable Class FooId 
    Private Shared _nextId As Integer 
    Private ReadOnly _id As Integer 

    Shared Sub New() 
        _nextId = 0 
    End Sub 

    Public Sub New() 
        SyncLock GetType(FooId) 
            _id = System.Math.Max(System.Threading.Interlocked.Increment(_nextId),_nextId - 1) 
        End SyncLock 
    End Sub 

    Public ReadOnly Property Id() As Integer 
        Get 
            Return _id 
        End Get 
    End Property 
End Class 

不是在Foo中存储int,而是存储FooId类型的对象。通过这种方式,您可以完全控制可以和不能对id执行的操作。

为了保护我们的FooId不被操纵,它不能被继承,并且除了构造函数和int的getter之外没有其他方法。此外,变量_nextId对FooId是私有的,不能从外部更改。最后,FooId构造函数中的SyncLock确保它永远不会在并行执行,从而保证进程内的所有ID都是唯一的(直到你点击MaxInt :))。

答案 2 :(得分:1)

ReadOnly变量必须在对象构造期间初始化,然后才能更新。这不会编译,因为你不能因为这个原因增加_nextId。 (共享ReadOnly变量只能在共享构造函数中指定。)

因此,如果你删除_nextId定义的ReadOnly修饰符,你应该没问题。

答案 3 :(得分:1)

我会这样做。

Public MustInherit Class Unique
    Private _UID As Guid = Guid.NewGuid()
    Public ReadOnly Property UID() As Guid
        Get
            Return _UID
        End Get
    End Property
End Class

答案 4 :(得分:0)

它会抛出一个错误,因为_nextId是ReadOnly。删除它。

修改 正如您所说,ReadOnly变量可以在构造函数中更改,但如果它们是Shared,则不会更改。这些只能在共享构造函数中更改。例如:

Shared Sub New()
   _nextId = 0
End Sub

答案 5 :(得分:0)

共享整数不应该是只读的。标记为readonly的字段只能分配一次,并且必须在构造函数退出之前分配。

由于共享字段是私有的,因此无论如何都不会有任何外部更改字段的危险。

答案 6 :(得分:0)

你说“这不会编译,因为它会抛出错误”,但从未说出错误是什么。

共享变量是静态的,因此在内存中只有一个副本可供所有实例访问。您只能从静态(共享)构造函数(New())修改静态只读(Shared ReadOnly),因此您可能需要这样的内容:

Public Class test
   Private Shared ReadOnly _nextId As Integer
   Private ReadOnly _id As Integer

   Public Shared Sub New()
      _nextId = _nextId + 1
   End Sub

   Public Sub New()
      _id = _nextId
   End Sub
End Class

(我认为这是VB中正确的语法。)在C#中它看起来像这样:

public class Test
{
   private static readonly int _nextId;
   private readonly int _id;

   static Test()
   {
      _nextId++;
   }

   public Test()
   {
      _id = _nextId;
   }

}

这里唯一的问题是静态构造函数只会被调用一次,因此_nextId只会增加一次。因为它是一个静态只读变量,所以你只能将它初始化为静态构造函数,因此你的新实例不会像你想要的那样获得递增的_id字段。

您尝试使用此方案解决的问题是什么?这些唯一ID必须是整数值吗?如果没有,你可以使用Guid并在你的构造函数中调用Guid。

答案 7 :(得分:0)

我发布了一个similar question,专注于设置唯一实例ID的多线程问题。您可以查看详细信息。

答案 8 :(得分:-1)

这可能会引发错误,因为您永远不会将_nextId初始化为任何内容。它需要有一个初始值才能安全地添加1。