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

时间:2020-03-05 18:53:45  来源:igfitidea点击:

每次实例化一个新的ID时,我都想给它一个唯一的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也是只读的,因此我们应该能够在构造函数中更改只读变量。)我认为这也与共享有关。任何解决方案(希望不是kludgy呵呵)或者对此无效的解释都可以接受。重要的是,在对象初始化之后,我希望两个变量(或者是否只有一种方法甚至更好,但我认为这是不可能的)是不可变的。谢谢!

解决方案

回答

可能会引发错误,因为我们从未将_nextId初始化为任何东西。必须先设置一个初始值,然后才能安全地添加1.

回答

因为_nextId为ReadOnly,它会引发错误。删除那个。

编辑:
如我们所说,ReadOnly变量可以在构造函数中更改,但如果是Shared则不能更改。这些只能在共享构造函数中进行更改。例子:

Shared Sub New()
   _nextId = 0
End Sub

回答

这种设计容易受到多线程问题的影响。我强烈建议对ID(Guid.NewGuid())使用Guids。如果我们绝对必须使用int,请签出Interlocked类。我们可以将所有递增和Id逻辑包装在一个基类中,以便仅在一个位置访问ID生成器。

回答

ReadOnly变量必须在对象构造期间初始化,然后不能再进行更新。这不会编译,因为由于这个原因我们不能增加_nextId。 (Shared ReadOnly变量只能在Shared构造函数中分配。)

这样,如果删除_nextId定义上的ReadOnly修饰符,则应该没问题。

回答

我会这样

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

回答

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

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

回答

考虑以下代码:

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为止)。

回答

我们说过"这不会编译,因为它会引发错误",但从未说过该错误是什么。

共享变量是静态的,因此内存中只有一个副本可供所有实例访问。我们只能从静态(Shared)构造函数(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中正确的语法。)在Cit中如下所示:

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

   static Test()
   {
      _nextId++;
   }

   public Test()
   {
      _id = _nextId;
   }

}

这里唯一的问题是静态构造函数将只被调用一次,因此_nextId仅将被递增一次。由于它是一个静态只读变量,因此我们只能使用静态构造函数对其进行初始化,因此新实例将不会像我们想要的那样获得递增的_id字段。

我们要在这种情况下解决的问题是什么?这些唯一ID是否必须为整数值?如果没有,我们可以使用Guid,并在构造函数中调用Guid。

回答

我发布了一个类似的问题,该问题集中在设置唯一实例ID的多线程问题上。我们可以查看它以获取详细信息。