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;
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
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. –
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. –