2011-09-16 11 views
37

Es posible que me pierda algún tipo de punto aquí, si ese es el caso, incluya esa discusión como parte de mi pregunta :).Enum con métodos de funcionalidad (Combine Class/Enum)

Esta es una muestra abreviada y renombrada de un código de trabajo. GetTicks (..) es una muestra única, que podría ser cualquier tipo de funcionalidad (el valor de > 0 < 9 debería devolver un Enum a.so específico).

public static class Something 
{ 
    public enum TypeOf : short 
    { 
     Minute = 2, Hour = 3, Day = 4, ........ 
    } 

    public static long GetTicks(Something.TypeOf someEnum) 
    { 
     long ticks = 0; 
     switch (someEnum) 
     { 
      case Something.TypeOf.Minute: 
       ticks = TimeSpan.TicksPerMinute; 
       break; 
      case Something.TypeOf.Hour: 
       ticks = TimeSpan.TicksPerHour; 
       break; 
     .... 
     } 
     return ticks; 
    } 
} 


// This class is called from anywhere in the system. 
public static void SomeMethod(string dodo, object o, Something.TypeOf period) 
{ 
    // With the design above 
    long ticks = Something.GetTicks(period); 

    // Traditional, if there was a simple enum 
    if (period == Something.Day) 
     ticks = TimeSpan.FromDays(1).Ticks; 
    else if (period == Something.Hour) 
     ticks = TimeSpan.FromHours(1).Ticks; 
} 

La idea es recoger la funcionalidad que se refiere a una enumeración, lo más cerca posible de la enumeración en sí. La enumeración es la función de razón. También me resulta fácil y natural buscar dicha funcionalidad cerca de la enumeración. También es fácil de modificar o ampliar.

El inconveniente que tengo, es que tengo que indicar la enumeración más explícita Something.TypeOf. El diseño puede parecer no estándar? y se aplicaría si la enumeración fuera para uso interno en la clase.

¿Cómo harías esto más agradable? Probé la herencia base abstracta, parcial. Ninguno de ellos parece aplicarse.

Respuesta

32

C# enumeraciones no funcionan bien de esta manera. Sin embargo, puede implementar su propia "conjunto fijo de valores" con bastante facilidad:

public sealed class Foo 
{ 
    public static readonly Foo FirstValue = new Foo(...); 
    public static readonly Foo SecondValue = new Foo(...); 

    private Foo(...) 
    { 
    } 

    // Add methods here 
} 

Si llega el caso, un ejemplo que tengo de esto es notablemente similar a la suya - DateTimeFieldType en Noda Time. En ocasiones, es posible que desee hacer que la clase se abra, pero conserve el constructor privado, que le permite crear las subclases solo como clases anidadas. Muy útil para restringir la herencia.

La desventaja es que no se puede usar el interruptor :(

+0

Completamente de hecho, notablemente igual! Me gusta su enfoque y profundizaré más en él cuando tenga tiempo para optimizarlo y comprender sus beneficios (si es que algunos);)). – Independent

+0

Sí, enumizaciones similares a Java. Aunque recomendaría usar una estructura, no una clase. Y un campo _Value_ para comparar rápidamente y la declaración de cambio. – IllidanS4

+0

@ IllidanS4: No veo particular para usar una estructura, y una clase permite diferentes subclases que implementan diferentes comportamientos (al igual que las enumeraciones de Java). Además, una estructura significa que 'default (Foo)' siempre es un valor "real", lo que puede ser un dolor en el cuello. Aunque estoy seguro de que hay casos en los que una versión de estructura sería apropiada, con mayor frecuencia uso clases. –

71

Si no te importa un poco más de la escritura se puede hacer un métodos de extensión para ampliar la interfaz de la enumeración.

por ejemplo

public enum TimeUnit 
{ 
    Second, 
    Minute, 
    Hour, 
    Day, 
    Year, 
    /* etc */ 
} 
public static class TimeUnitExtensions 
{ 
    public static long InTicks(this TimeUnit myUnit) 
    { 
     switch(myUnit) 
     { 
      case TimeUnit.Second: 
       return TimeSpan.TicksPerSecond; 
      case TimeUnit.Minute: 
       return TimeSpan.TicksPerMinute; 
      /* etc */ 
     } 
    } 
} 

Esto puede agregar métodos "instancia" a sus enumeraciones. es un poco más detallado de lo más parecido sin embargo.

Recuerde, sin embargo una enumeración debe tratarse en su mayoría como un valor con nombre.

+0

¿Cómo se vincula 'TimeUnitExtensions' a' TimeUnit' y la instancia 'TimeSpan' para la que desea los tics? Puede que me falta algo de contexto y sin dudas mi falta de conocimiento de C# no está ayudando. –

+2

@DavidHarkness, es por el "esto" en esta línea: public static long InTicks (esta TimeUnit myUnit). El "este" asocia TimeUnit a la extensión. – Seva

+0

Me gusta la respuesta de Jon, pero esta también es una posibilidad. +1, ¡no había considerado los métodos de extensión para esto! – Joel