2009-02-23 3 views

risposta

46

Le enumerazioni devono essere sicure. Penso che non li abbiano resi implicitamente capaci di scoraggiare altri usi. Sebbene il framework ti permetta di assegnare loro un valore costante, dovresti riconsiderare le tue intenzioni. Se si utilizza in primo luogo l'enum per la memorizzazione di valori costanti, considerare l'utilizzo di una classe statica:

public static class ReturnValue 
{ 
    public const int Success = 0; 
    public const int FailReason1 = 1; 
    public const int FailReason2 = 2; 
    //Etc... 
} 

che ti permette di fare questo.

public static int main(string[] args){ 
    return ReturnValue.Success; 
} 

EDIT

Quando si fai vogliono fornire i valori di un enum è quando si desidera combinare loro. Vedere l'esempio di seguito:

[Flags] // indicates bitwise operations occur on this enum 
public enum DaysOfWeek : byte // byte type to limit size 
{ 
    Sunday = 1, 
    Monday = 2, 
    Tuesday = 4, 
    Wednesday = 8, 
    Thursday = 16, 
    Friday = 32, 
    Saturday = 64, 
    Weekend = Sunday | Saturday, 
    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday 
} 

Questo enum può essere quindi utilizzato utilizzando la matematica bit per bit. Vedere l'esempio di seguito per alcune applicazioni.

public static class DaysOfWeekEvaluator 
{ 
    public static bool IsWeekends(DaysOfWeek days) 
    { 
     return (days & DaysOfWeek.Weekend) == DaysOfWeek.Weekend; 
    } 

    public static bool IsAllWeekdays(DaysOfWeek days) 
    { 
     return (days & DaysOfWeek.Weekdays) == DaysOfWeek.Weekdays; 
    } 

    public static bool HasWeekdays(DaysOfWeek days) 
    { 
     return ((int) (days & DaysOfWeek.Weekdays)) > 0; 
    } 

    public static bool HasWeekendDays(DaysOfWeek days) 
    { 
     return ((int) (days & DaysOfWeek.Weekend)) > 0; 
    } 
} 
+0

Funziona come un incantesimo, grazie! Strano che tu possa definire un enum: "public enum MyEnum: int {...}" e non avere i suoi valori in modo implicito. Non esiste un concetto di "enum generico"? - public enum MyEnum - o è completamente ridicolo? – Pwninstein

+0

Puoi delcare digitare sul tuo enum "public enum ReturnValue: int" che impone che i valori siano di tipo int. Non fornisce ancora il casting implicito. Modifica per mostrare quando è una buona idea usare i valori in un enum. –

+2

Un suggerimento minore - usa 'static readonly int' piuttosto che' const', nel caso in cui i valori cambino nelle versioni future. Non vuoi che i chiamanti abbiano quel valore compilato nel loro codice. –

2

Enums e ints sono semplicemente non casable implicitamente secondo le specifiche (eccetto per lo 0 letterale, che è consentito per test di confronto/assegnazioni/etc). Il cast esplicito è tutto ciò che è necessario, però.

+2

(spec 13.1.3 e 13.2.2 di ECMA 334v4) –

+0

Sarebbe bello se si potesse ignorare l'operatore di uguaglianza per le enumerazioni –

+0

@ Chris si potrebbe scrivere un metodo di estensione , Forse. Non proprio la stessa cosa, ma ... –

0

No, non è possibile evitare il casting; sul perché non c'è una conversione implicita, non lo so, ma non c'è.

4

Non c'è cast implicito perché l'enumerazione non deve usare int come tipo sottostante. Se il tuo enum ha usato un uint come tipo sottostante, ad esempio, non c'è cast implicito da uint a int.

+1

Due upvotes? Trovo questa risposta inutile in quanto risponde alla domanda che è stata scritta, ma non all'ovvia domanda che è stata posta. Cosa succede se si definisce 'enum MyEnum: int {}' ?; quindi non deve, ma effettivamente usa int come tipo sottostante; e non c'è ancora un casting implicito. sadpanda :( – ANeves

0

Stranamente, questo non è specifico per .NET Framework, ma solo per C#. Come gli altri commentatori hanno già sottolineato, in C# questa è fondamentalmente una specifica della lingua. Lo stesso non è vero in VB.NET.

Controllare la pagina di riferimento MSDN per Enums in VB.NET. Si noti che è possibile specificare il tipo di dati di un'enumerazione al momento della dichiarazione Enum.

Ciò significa che, se si davvero non vogliono lettiera il codice con calchi di (int), si potrebbe scrivere il censimento nel VB.NET, dichiararla come un intero, quindi utilizzare tale Enum da C#.

Ricordate come ci hanno detto che i computer avrebbero reso le nostre vite molto più semplici? :)

+0

Qui incontrerai lo stesso problema, visto che stai consumando l'enum in C#, e ricorda, C# non può convertire implicitamente l'enum! Sei tornato al punto di partenza;) –

0

È possibile attribuire questo comportamento all'intenzione di base alla creazione di Enumerazioni ... per creare un insieme di costanti denominate che possono avere solo valori specificati (o predefiniti) a seconda del tipo sottostante.

Ci sono due questioni separate da prendere in considerazione, in quanto legati alla tua domanda:

  1. Un valore Enum non può essere trattata come un int per default, perché allora si sarebbe in grado di fornire qualsiasi intero e là non sarebbe un controllo del tempo di compilazione per convalidare che il numero intero fornito esiste in realtà come valore nell'enumerazione.

  2. Casting diventa necessario poiché si sta tentando di convertire dal tipo di governo (di tipo YourCustomEnum che deriva dalla classe System.Enum) per il tipo sottostante, cioè, int o byte, ecc

4

L' c enum è inutile.

È possibile evitare il casting dal tipo E vincolare i valori che possono essere espressi nel cast del tipo eseguendo una classe sealed e fornendo operatori di conversione implicita/esplicita.

  • Fornire un operatore implicito per la conversione dal tuo tipo a un int generico in modo da non dover eseguire il cast.
  • Fornire un operatore esplicito per la conversione da un int al tipo, che genera un errore se il numero intero non riesce a soddisfare il vincolo, ad esempio (int x) => (x> = 0 & & x < = 2).

Se si utilizza questa tecnica, creare una generica classe base immutabile come ConstrainedNumber<T>, che ha un costruttore che accetta un valore T e delegato per il vincolo: delegate bool NumberConstraint<T>(T value). Il costruttore deve eseguire il valore tramite il delegato del vincolo e generare un'eccezione se non riesce a soddisfare il vincolo. La classe base dovrebbe anche occuparsi dell'operazione di conversione implicita su T, e dovrebbe gestire l'uguaglianza sovraccaricando object.Equals (oggetto) e object.GetHashCode(), definendo == e! = Operatori per il tipo ConstrainedNumber<T> e implementando IEquatable<T> e IEquatable<ConstrainedNumber<T>>. Raccomando anche di definire un costruttore di copia per la classe base e tutti i tipi derivati. La clonazione può quindi essere implementata in modo pulito nella classe base recuperando il costruttore di copie tramite reflection, ma questo è del tutto facoltativo. Puoi capire da solo l'implementazione ConstrainedNumber<T>, a meno che non l'abbia già pubblicata su StackOverflow da qualche parte.

È possibile fornire valori readon statici denominati nel numero vincolato derivato, in modo che sia possibile accedervi come un enum.

public sealed class ReturnValue: ConstrainedNumber<int> 
{ 
    public static readonly NumberConstraint<int> constraint = (int x) => (x >= 0 && x < 3); 

    public static readonly ReturnValue Success = new ReturnValue(0); 
    public static readonly ReturnValue FailReason1 = new ReturnValue(1); 
    public static readonly ReturnValue FailReason2 = new ReturnValue(2); 

    private ReturnValue(int value): base(value, constraint) {} 
    private ReturnValue(ReturnValue original): base (original) {} //may be used to support IClonable implementation in base class 
    public static explicit operator ReturnValue(int value) 
    { 
     switch(value) //switching to return an existing instance is more efficient than creating a new one and re-checking the constraint when there is a limited number of allowed values; if the constraint was more generic, such as an even number, then you would instead return a new instance here, and make your constructors public. 
     { 
      case 0: return Success; 
      case 1: return FailReason1; 
      case 2: return FailReason2; 
     } 
     throw new ArgumentException("Value fails to meet the constraint defined for " + typeof(ReturnValue).FullName + ".", "value"); 
    } 

} 

È possibile utilizzare questa tecnica per qualsiasi vincolo. Ad esempio, una classe chiamata EvenNumber può avere un vincolo che restituisce true se il numero specificato è pari. In tal caso, devi solo rendere pubblici i tuoi costruttori e semplificare l'operatore di conversione statico per restituire un nuovo numero EvenNumber, anziché passare per restituire una delle istanze limitate esistenti.

potrebbe essere utilizzato in questo modo:

EvenNumber x = (EvenNumber)2; 
EvenNumber y = (EvenNumber)3; //throws exception "Value fails to meet the constraint defined for {namespace}.EvenNumber." A c# enum would stupidly allow such a cast, creating an invalid EvenNumber, breaking the object-oriented model 
int z = x; //implicit conversion, no cast necessary; 
-2

Come sull'utilizzo membri statici di una classe?

//enum DocInfos { DocName, DocNumber, DocVersion}; 
public class DocInfos 
{ 
    public static int DocName = 0; 
    public static int DocNumer = 1; 
    public static int DocVersion = 2; 
} 

...

  Doc = new string[DocInfos.DocVersion]; 
      // Treffer 
      Doc[DocInfos.DocName] = TrimB(HTMLLines[lineCounter + 2]) 

...

+0

Solo FYI - Non ho votato questo, ma scommetto che è stato votato giù b/c è sia ridondante della risposta votata in alto sia errata. La classe DocInfos dovrebbe essere statica. –