2008-09-15 5 views
2

Je souhaite attribuer à une classe un ID unique chaque fois qu'une nouvelle ID est instanciée. Par exemple, avec une classe nommée Foo je voudrais être en mesure de faire ce qui suitAttribution d'un ID unique de classe à l'instanciation: .Net

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

et recevrais un identifiant unique et b obtiendrais un ID unique. Les identifiants doivent seulement être uniques au cours de l'exécution, donc je voudrais juste utiliser un nombre entier. J'ai trouvé un moyen de le faire MAIS (et voici la mise en garde) Je ne veux pas être en mesure de changer l'ID de n'importe où. Mon idée actuelle un moyen de mettre en œuvre c'est le suivant:

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 

Cependant, cela ne compile pas parce qu'il renvoie une erreur sur _nextId = _nextId + 1 Je ne vois pas pourquoi ce serait une erreur (parce que _Id est aussi en lecture seule, vous êtes supposé être capable de changer une variable en lecture seule dans le constructeur.) Je pense que cela a quelque chose à voir avec le fait qu'il soit partagé aussi. Toute solution (espérons pas kludgy hehe) ou une explication de pourquoi cela ne fonctionnera pas sera acceptée. La partie importante est que je veux que les deux variables (ou s'il y a un moyen d'en avoir un qui serait encore mieux mais je ne pense pas que cela soit possible) soient immuables après l'initialisation de l'objet. Merci!

Répondre

2

Consultez le code suivant:

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 

Au lieu de stocker un int intérieur Foo, vous stockez un objet de type fooid. De cette façon, vous avez un contrôle total sur ce qui peut et ne peut pas être fait sur l'identifiant.

Pour protéger notre FooId contre la manipulation, il ne peut pas être hérité, et n'a pas de méthodes sauf le constructeur et un getter pour l'int. De plus, la variable _nextId est privée à FooId et ne peut pas être modifiée de l'extérieur. Enfin, le SyncLock dans le constructeur de FooId s'assure qu'il n'est jamais exécuté parallèlement, garantissant que tous les ID d'un processus sont uniques (jusqu'à ce que vous atteigniez MaxInt :)).

+0

Verrouiller sur l'objet type est une mauvaise idée. Tout le monde peut obtenir l'objet type et le verrouiller. Les objets de verrouillage doivent rester privés dans une instance de classe. En outre, le verrouillage et l'utilisation d'Interlocked.Increment sont redondants. les méthodes de classe Interlocked sont atomiques et ne nécessitent pas de verrouillage. – Will

-1

Il y a probablement une erreur car vous n'initialisez jamais _nextId à quoi que ce soit. Il doit avoir une valeur initiale avant de pouvoir ajouter 1 en toute sécurité.

0

Il génère une erreur car _nextId est ReadOnly. Enlevez ça.

Modifier: Comme vous le dites, les variables ReadOnly peuvent être modifiées dans un constructeur, mais pas si elles sont partagées. Ceux-ci ne peuvent être modifiés que dans les constructeurs partagés. Exemple:

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

Avez-vous lu la question? Il veut que _nextId soit en lecture seule. – TheSmurf

+0

Je ne savais pas que vous pouviez commenter les réponses, alors ignorez ma réponse ci-dessous, cela a du sens. Je laisse cela ouvert pour voir si quelqu'un d'autre a des idées intéressantes mais j'accepterai cette réponse. Merci je ne savais pas qu'une lecture partagée ne pouvait être modifiée que dans un constructeur partagé. – Nicholas

5

Cette conception est vulnérable aux problèmes de multithreading. Je suggère fortement d'utiliser Guids pour vos identifiants (Guid.NewGuid()). Si vous devez absolument utiliser ints, consultez la classe Interlocked. Vous pouvez inclure toutes les logiques d'incrémentation et d'identification dans une classe de base de sorte que vous n'accédiez au générateur d'ID que dans un seul emplacement.

1

Les variables ReadOnly doivent être initialisées lors de la construction de l'objet, puis ne peuvent pas être mises à jour par la suite. Cela ne compilera pas car vous ne pouvez pas incrémenter _nextId pour cette raison. (Les variables ReadOnly partagées ne peuvent être affectées que dans les constructeurs partagés.)

Ainsi, si vous supprimez le modificateur ReadOnly sur la définition de _nextId, cela devrait fonctionner.

1

Je le ferais comme ça.

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

L'entier partagé ne doit pas être en lecture seule. Un champ marqué readonly ne peut être affecté qu'une seule fois et doit être affecté avant que le constructeur ne quitte. Comme le champ partagé est privé, il n'y a aucun risque que le champ soit modifié par quoi que ce soit d'autre.

0

Vous avez dit que "ceci ne compilera pas car il lance une erreur" mais n'a jamais dit quelle est cette erreur. Une variable partagée est statique, donc il n'y a qu'une seule copie en mémoire accessible à toutes les instances. Vous ne pouvez pas modifier une lecture seule statique (Shared ReadOnly) à partir d'un constructeur statique (Shared) (Nouveau()) si vous voulez probablement quelque chose comme ceci:

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 

(. Je pense que c'est la bonne syntaxe dans VB) Dans C#, il ressemblerait à ceci:

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

    static Test() 
    { 
     _nextId++; 
    } 

    public Test() 
    { 
     _id = _nextId; 
    } 

}

le seul problème ici est que le constructeur statique ne va être appelé une fois, si _nextId ne va incrémenter une fois. Comme il s'agit d'une variable statique en lecture seule, vous ne pourrez l'initialiser que sur le constructeur statique, de sorte que vos nouvelles instances n'obtiendront pas un champ _id incrémenté comme vous le souhaitez.

Quel est le problème que vous essayez de résoudre avec ce scénario? Ces ID uniques doivent-ils être des valeurs entières? Si ce n'est pas le cas, vous pouvez utiliser un Guid et dans votre appel de contructor Guid.

0

J'ai posté un similar question qui se concentrait sur les problèmes de multithreading de la définition d'un ID d'instance unique. Vous pouvez l'examiner pour plus de détails.

Questions connexes