2009-02-23 10 views
27

Si tengo la siguiente enumeración:¿Puedo evitar emitir un valor enum cuando trato de usarlo o devolverlo?

public enum ReturnValue{ 
    Success = 0, 
    FailReason1 = 1, 
    FailReason2 = 2 
    //Etc... 
} 

¿Puedo evitar la proyección cuando regrese, así:

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

Si no es así, ¿por qué no es un valor de enumeración tratado como un entero por defecto ?

Respuesta

46

se supone que las enums son de tipo seguro. Creo que no los hicieron implícitamente convertibles para desalentar otros usos. Aunque el marco le permite asignarles un valor constante, debe reconsiderar su intención. Si se utiliza principalmente para el almacenamiento de la enumeración de valores constantes, considerar el uso de una clase estática:

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

que le permite hacer esto.

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

EDITAR

Cuando haces desea proporcionar valores de una enumeración es cuando se quiere combinarlos. Véase el siguiente ejemplo:

[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 
} 

Esta enumeración continuación, puede ser consumido por el uso de las matemáticas a nivel de bit. Vea el ejemplo a continuación para algunas aplicaciones.

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

¡Funciona como un amuleto, gracias! Es extraño que pueda definir una enumeración: "public enum MyEnum: int {...}" y no tener sus valores implícitamente convertibles. No hay concepto de una "enumeración genérica" ​​está allí? - enum pública MyEnum - ¿o es completamente ridículo? – Pwninstein

+0

Puede delcare type en su enumeración "public enum ReturnValue: int" que exige que los valores sean del tipo int. Todavía no proporciona conversión implícita. Editando para mostrar cuándo es una buena idea usar valores en una enumeración. –

+2

Una pequeña sugerencia: use 'static readonly int' en vez de' const', en caso de que sus valores cambien en versiones futuras. No desea que las personas que llaman tengan ese valor compilado en su código. –

2

Enumeraciones y entradas simplemente no son moldeables implícitamente según la especificación (excepto para el literal 0, que está permitido para pruebas de comparación/asignaciones/etc.). El yeso explícito es todo lo que se necesita, sin embargo.

+2

(especificación 13.1.3 y 13.2.2 de ECMA 334v4) –

+0

Sería bueno si pudiera anular el operador de igualdad para las enumeraciones –

+0

@ Chris se podría escribir un método de extensión , quizás. No es exactamente lo mismo, pero ... –

0

No, no puedes evitar el casting; en cuanto a por qué no hay conversión implícita, no sé, pero no los hay.

4

No hay conversión implícita porque la enumeración no tiene que usar int como el tipo subyacente. Si su enumeración usó un uint como el tipo subyacente, por ejemplo, no hay conversión implícita de uint a int.

+1

¿Dos votaciones ascendentes? Encuentro esta respuesta inútil porque responde la pregunta que se escribió, pero no la pregunta obvia que se estaba haciendo. ¿Qué sucede si define 'enum MyEnum: int {}' ?; entonces no tiene que hacerlo, pero efectivamente usa int como tipo subyacente; y todavía no hay un casting implícito. sadpanda :( – ANeves

0

Curiosamente, esto no es específico de .NET Framework, sino solo para C#. Como los otros comentaristas ya han señalado, en C# esto es básicamente una especificación del lenguaje. Lo mismo no es cierto en VB.NET.

Consulte la página de referencia de MSDN para Enums in VB.NET. Tenga en cuenta que puede especificar el tipo de datos de una enumeración en el tiempo de declaración de Enum.

Eso significa que, si realmente no quieren a la basura su código con moldes a (int), se podría escribir su enumeración en VB.NET, declararemos como un entero, a continuación, utilizar esa enumeración de C#.

¿Recuerdas cómo nos dijeron que las computadoras nos harían la vida mucho más simple? :)

+0

Te encontrarás con el mismo problema aquí, ya que estás consumiendo la enumeración en C#, y recuerda, C# no puede convertir implícitamente la enumeración. Has vuelto al punto cero;) –

0

Puede atribuir este comportamiento a la intención básica de crear Enumeraciones ... para crear un conjunto de constantes con nombre que solo pueden tener valores especificados (o predeterminados) según el tipo subyacente.

Hay dos cuestiones diferentes a tener en cuenta, en relación con su pregunta:

  1. Un valor Enum no puede ser tratado como un entero por defecto porque entonces sería capaz de proporcionar cualquier número entero y hay no sería una verificación de tiempo de compilación para validar que el entero proporcionado realmente existe como un valor en la enumeración.

  2. Fundición hace necesario ya que está intentando convertir desde el tipo de gobierno (de tipo YourCustomEnum que se deriva de la clase System.Enum) al tipo subyacente, es decir, int o byte, etc.

4

El C# enum es inútil.

Puede evitar la conversión desde su tipo Y restringir los valores que se pueden convertir explícitamente a su tipo realizando una clase sellada y proporcionando operadores de conversión implícita/explícita.

  • Proporcione un operador implícito para convertir de su tipo a un int genérico para que no tenga que emitir.
  • Proporcione un operador explícito para convertir de un int a su tipo, que arroja un error si el entero no cumple la restricción, como (int x) => (x> = 0 & & x < = 2).

Si se utiliza esta técnica, crear una clase base inmutable genérico como ConstrainedNumber<T>, que tiene un constructor que acepta un valor de T y delegado para la restricción: delegate bool NumberConstraint<T>(T value). El constructor debe ejecutar el valor a través del delegado de restricción y lanzar una excepción si no cumple con la restricción. La clase base también debe encargarse de la operación de conversión implícita a T, y debe manejar la igualdad mediante la sobrecarga de object.Equals (object) y object.GetHashCode(), definiendo == y! = Operadores para el tipo ConstrainedNumber<T>, e implementando IEquatable<T> y IEquatable<ConstrainedNumber<T>>. También recomiendo definir un constructor de copia para la clase base y todos los tipos derivados. La clonación se puede implementar limpiamente en la clase base recuperando el constructor de copia mediante reflexión, pero esto es completamente opcional. Puede averiguar la implementación de ConstrainedNumber<T> usted mismo, a menos que ya la haya publicado en stackoverflow en alguna parte.

Puede proporcionar valores nominales de solo lectura estáticos en su Número restringido derivado, para que pueda acceder a ellos como una enumeración.

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"); 
    } 

} 

Puede utilizar esta técnica para cualquier restricción. Por ejemplo, una clase llamada EvenNumber puede tener una restricción que devuelve verdadero si el número dado es par. En ese caso, solo haría público sus constructores y simplificaría su operador de conversión estático para que simplemente devuelva un nuevo EvenNumber, en lugar de cambiar para devolver una de las instancias existentes limitadas.

Podría ser utilizado como esto:

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

¿Cómo sobre el uso de los miembros estáticos de una clase?

//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 - No voté esto, pero apuesto a que fue rechazado porque es redundante de la respuesta más alta e incorrecta. La clase DocInfos debe ser estática. –

Cuestiones relacionadas