2008-09-15 6 views
2

Ich möchte jeder Klasse eine eindeutige ID geben, wenn eine neue Instanz instanziiert wird. Zum Beispiel mit einer Klasse namens Foo i möchte folgendeGeben Sie die eindeutige ID der Klasse bei Instanziierung ein: .Net

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

und eine bekommen würde eine eindeutige ID und b in der Lage sein würde, eine eindeutige ID erhalten zu tun. Die IDs müssen nur während der Laufzeit eindeutig sein, daher möchte ich nur eine Ganzzahl verwenden. Ich habe einen Weg gefunden, dies zu tun (und den Vorbehalt) Ich möchte nicht in der Lage sein, die ID von überall zu ändern. Meine aktuelle Idee für einen Weg, dies zu implementieren ist der folgende:

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 

Allerdings wird dies nicht kompilieren, weil es einen Fehler auf wirft _nextId = _nextId + 1 Ich sehe nicht, warum dies ein Fehler sein würde (Da _Id auch nur gelesen werden kann, sollte es möglich sein, eine schreibgeschützte Variable im Konstruktor zu ändern.) Ich denke, das hat etwas damit zu tun, dass es auch geteilt wird. Jede Lösung (hoffentlich nicht kludgy hehe) oder eine Erklärung, warum das nicht funktioniert, wird akzeptiert. Der wichtige Teil ist, dass ich will, dass beide Variablen (oder wenn es einen Weg gibt, nur einen zu haben, der sogar besser wäre, aber ich denke nicht, dass das möglich ist) nach der Initialisierung des Objekts unveränderlich sind. Vielen Dank!

Antwort

2

Betrachten Sie den folgenden Code ein:

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 

Statt einen int innerhalb Foo zu speichern, speichern Sie ein Objekt vom Typ FooId. Auf diese Weise haben Sie die volle Kontrolle darüber, was mit der ID gemacht werden kann und was nicht.

Um unsere FooId gegen Manipulation zu schützen, kann sie nicht vererbt werden und hat keine Methoden außer dem Konstruktor und einem Getter für den int. Außerdem ist die Variable _nextId für FooId privat und kann nicht von außen geändert werden. Schließlich stellt die SyncLock innerhalb des Konstruktors von FooId sicher, dass sie niemals parallel ausgeführt wird und garantiert, dass alle IDs innerhalb eines Prozesses eindeutig sind (bis Sie MaxInt :) treffen).

+0

Das Sperren des Typs Objekt ist eine schlechte Idee. Jeder kann das Typ-Objekt bekommen und es sperren. Lock-Objekte sollten innerhalb einer Klasseninstanz privat gehalten werden. Außerdem ist das Sperren und anschließende Verwenden von Interlocked.Increment überflüssig. Die Interlocked-Klassenmethoden sind atomar und müssen nicht gesperrt werden. – Will

-1

Es ist wahrscheinlich ein Fehler, weil Sie niemals _nextId zu etwas initialisieren. Es muss einen Anfangswert haben, bevor Sie sicher 1 hinzufügen können.

0

Es wird ein Fehler ausgegeben, weil _nextId ReadOnly ist. Entferne das.

Bearbeiten: Wie Sie sagen, ReadOnly Variablen können in einem Konstruktor geändert werden, aber nicht, wenn sie freigegeben sind. Diese können nur in gemeinsam genutzten Konstruktoren geändert werden. Beispiel:

Shared Sub New() 
    _nextId = 0 
End Sub 
+0

Haben Sie die Frage gelesen? Er möchte _nextId nur lesen. – TheSmurf

+0

Ich wusste nicht, dass Sie Antworten kommentieren können, ignorieren Sie also meine Antwort darunter, das macht Sinn. Ich lasse das für ein bisschen offen, um zu sehen, ob jemand andere interessante Ideen hat, aber dann werde ich diese Antwort akzeptieren. Danke, ich wusste nicht, dass eine gemeinsame Lese nur in einem gemeinsamen Konstruktor geändert werden konnte. – Nicholas

5

Dieses Design ist anfällig für Multithreading-Probleme. Ich würde dringend empfehlen, Guids für Ihre IDs zu verwenden (Guid.NewGuid()). Wenn Sie unbedingt Intars verwenden müssen, überprüfen Sie die Interlocked Klasse. Sie können alle Inkrementierungs- und Id-Logik in einer Basisklasse umbrechen, sodass Sie nur an einem Ort auf den ID-Generator zugreifen.

1

ReadOnly-Variablen müssen während der Objektkonstruktion initialisiert und anschließend nicht aktualisiert werden. Dies wird nicht kompiliert, da Sie _nextId aus diesem Grund nicht erhöhen können. (Gemeinsame ReadOnly-Variablen können nur in Shared-Konstruktoren zugewiesen werden.)

Wenn Sie also den ReadOnly-Modifizierer in der Definition von _nextId entfernen, sollten Sie in Ordnung sein.

1

Ich würde es so machen.

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 
0

Die gemeinsame Ganzzahl sollte nicht schreibgeschützt sein. Ein Feld mit der Bezeichnung readonly kann immer nur einmal vergeben werden und muss zugewiesen werden, bevor der Konstruktor beendet wird.

Da das Feld "shared" privat ist, besteht keine Gefahr, dass das Feld von irgendwas externem geändert wird.

0

Sie sagten, dass "dies nicht kompiliert wird, weil es einen Fehler auslöst", aber nie gesagt hat, was dieser Fehler ist.

Eine gemeinsam genutzte Variable ist statisch, sodass nur eine einzige Kopie davon im Speicher für alle Instanzen verfügbar ist. Sie können nur eine statische Nur-Lese (Geteilt Readonly) von einem statischen (Geteilt) Konstruktor (New()) ändern, so dass Sie wahrscheinlich so etwas wie dies wollen:

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 

(. Ich denke, das ist die richtige Syntax in VB) In C# es würde wie folgt aussehen:

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

    static Test() 
    { 
     _nextId++; 
    } 

    public Test() 
    { 
     _id = _nextId; 
    } 

}

das einzige Problem dabei ist, dass der statische Konstruktor wird nur einmal aufgerufen werde, so _nextId nur geht zu einer Zeit erhöht werden. Da es sich um eine statische Readonly-Variable handelt, können Sie den statischen Konstruktor nur initialisieren, damit Ihre neuen Instanzen kein inkrementiertes _id-Feld erhalten, wie Sie möchten.

Was ist das Problem, das Sie versuchen, mit diesem Szenario zu lösen? Müssen diese eindeutigen IDs Integer-Werte sein? Wenn nicht, könnten Sie eine Guid und in Ihrem Contructor Guid verwenden.

0

Ich postete eine similar question, die auf die Multithreading-Probleme der Einstellung einer eindeutigen Instanz-ID konzentriert. Sie können es für Details überprüfen.

Verwandte Themen