2009-11-30 4 views
0

J'essaie d'utiliser le code de cet article: Creating Fake Enums, mais je ne peux pas comprendre pourquoi cela ne fonctionne pas.Fake enum en C#

Ce code:

Console.WriteLine(FakeEnum.One.FriendlyName); 
Console.WriteLine(FakeEnum.Four.FriendlyName); 

génère une exception:

System.TypeInitializationException was unhandled 
    Message="The type initializer for 'FakeEnum' threw an exception." 
    Source="FakeEnum1" 
    TypeName="FakeEnum" 
    StackTrace: 
     at FakeEnum1.Program.Main(String[] args) in ..\Test\FakeEnum1\Program.cs:line 26 
     at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) 
     at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
     at System.Threading.ThreadHelper.ThreadStart() 
    InnerException: System.NullReferenceException 
     Message="Object reference not set to an instance of an object." 
     Source="FakeEnum1" 
     StackTrace: 
      at FakeEnum.op_Equality(FakeEnum a, FakeEnum b) in ..\Test\FakeEnum1\FakeEnum.cs:line 158 
      at FakeEnum.ToString(String format) in ..\Test\FakeEnum1\FakeEnum.cs:line 31 
      at FakeEnum.ToString() in ..\Test\FakeEnum1\FakeEnum.cs:line 25 
      at FakeEnum..ctor(Int32 value, String friendlyName) in ..\Test\FakeEnum1\FakeEnum.cs:line 171 
      at FakeEnum..ctor(Int32 value) in ..\Test\FakeEnum1\FakeEnum.cs:line 165 
      at FakeEnum..cctor() in ..\Test\FakeEnum1\FakeEnum.cs:line 13 

Si je commente les membres déclarés avec le constructeur (int), tout le reste fonctionne:

public static readonly FakeEnum One = new FakeEnum(1, "One's Friendly Name"); 
public static readonly FakeEnum Two = new FakeEnum(2, "Two's Friendly Name"); 
public static readonly FakeEnum Three = new FakeEnum(3, "Three's Friendly Name"); 
//public static readonly FakeEnum Four = new FakeEnum(4); 
//public static readonly FakeEnum Five = new FakeEnum(5); 
//public static readonly FakeEnum Six = new FakeEnum(6); 

Maintenant, si Je fais les constructeurs public, le code suivant fonctionne juste f ine:

FakeEnum a = new FakeEnum(14, "1 4"); 
FakeEnum b = new FakeEnum(28); 
Console.WriteLine(a.FriendlyName); 
Console.WriteLine(b.FriendlyName); 

Je manque juste d'idées - qu'est-ce que je manque, et qu'est-ce qui génère l'exception lors de l'utilisation du code original?

Répondre

7

Il est ce bit:

FakeEnum temp = staticField.GetValue(null) as FakeEnum; 
if(temp == null) continue; 

que "==" appelle l'opérateur ==, qui ne prévoit pas le côté gauche pour être nulle:

public static bool operator == (FakeEnum a, FakeEnum b) { 
    return a.Equals(b); 
} 

Vous pouvez fixer l'opérateur comme ceci:

public static bool operator == (FakeEnum a, FakeEnum b) { 
    if (object.ReferenceEquals(a, b)) { 
     return true; 
    } 
    if (object.ReferenceEquals(a, null) || object.ReferenceEquals(b, null)) { 
     return false; 
    } 
    return a.Equals(b); 
} 

(! Ou en utilisant object.Equals, comme l'a souligné dans une autre réponse - dOH) 012 Ou vous pouvez le modifier pour que le délégué Equals devienne == au lieu de l'inverse.

Personnellement, je dirais que cette classe a l'air un peu poilue. Il n'est même pas clair pour moi pourquoi == et! = Besoin de surcharge ou bien pourquoi Equals a besoin de surcharger à moins que vous ayez vraiment deux objets séparés avec la même valeur (par exemple en raison de la sérialisation, qui va être icky de toute façon).

+0

Pouvez-vous me dire pourquoi la déclaration de FakeEnum.Four déclenche l'exception, mais ne déclare pas une nouvelle variable utilisant le même constructeur? – alexandrul

+2

@alexandrul: Parce qu'il regarde les champs de l'énumération * pendant que le type est encore en cours d'initialisation *. Par conséquent, certains de ces champs sont null, ce qui déclenche le problème. Cette classe essaie d'être trop intelligente pour son propre bien, IMO. –

+0

(A ce propos, rendre les constructeurs publics supprime un point important d'une énumération - cela doit être un * ensemble de valeurs fixe *.) –

5

Je ne l'ai pas vraiment essayé moi-même, mais les opérations == et! = Ne sont définitivement pas implémentées correctement. Devrait être:

public static bool operator == (FakeEnum a, FakeEnum b) { 
    return object.Equals(a,b); 
} 
public static bool operator != (FakeEnum a, FakeEnum b) { 
    return !object.Equals(a,b); 
} 
1

Object.equals appelle l'opérateur == de l'objet (qui appeler la méthode Object.equals qui remettrait les objets (qui appeler ...)) et tout ce que vous seriez à gauche avec est un débordement de pile.

il appelle la méthode equals de l'objet pour tester la valeur entière sous-jacente afin de simuler le comportement d'une instance enum.