2009-06-11 11 views
43

Tengo un método que usa un IList<T> como parámetro. Necesito comprobar cuál es el tipo de ese objeto T y hacer algo basado en él. Intentaba usar el valor T, pero el compilador no lo permite. Mi solución es la siguiente:C# Generics and Type Checking

private static string BuildClause<T>(IList<T> clause) 
{ 
    if (clause.Count > 0) 
    { 
     if (clause[0] is int || clause[0] is decimal) 
     { 
      //do something 
     } 
     else if (clause[0] is String) 
     { 
      //do something else 
     } 
     else if (...) //etc for all the types 
     else 
     { 
      throw new ApplicationException("Invalid type"); 
     } 
    } 
} 

Tiene que haber una forma mejor de hacerlo. ¿Hay alguna manera de que pueda verificar el tipo de T que se transfiere y luego usar una declaración switch?

+1

yo personalmente me gustaría saber lo que estás haciendo especial para cada tipo de datos. Si realiza aproximadamente la misma transformación para cada tipo de datos, podría ser más fácil asignar diferentes tipos a una interfaz común y operar en esa interfaz. – Juliet

Respuesta

68

usted podría utilizar sobrecargas:

public static string BuildClause(List<string> l){...} 

public static string BuildClause(List<int> l){...} 

public static string BuildClause<T>(List<T> l){...} 

O puede inspeccionar el tipo del parámetro genérico:

Type listType = typeof(T); 
if(listType == typeof(int)){...} 
+14

+1: las sobrecargas son definitivamente la * mejor * solución aquí en términos de diseño y mantenimiento a largo plazo. Una verificación de tipo de tiempo de ejecución de un parámetro genérico parece demasiado irónica para codificar con una cara seria. – Juliet

+0

Un gran ejemplo de cuándo esto sería útil es la serialización genérica con tipos muy variables. Si el objeto que se pasa es una cadena, ¿por qué funciona extra? Si se trata de una cadena, simplemente devuelva la cadena original sin intentar ningún procesamiento adicional – watkinsmatthewp

+0

Lo sentimos, ¿hay alguna manera de lograr eso mediante el uso de 'switch-case' en lugar de' if-else'? –

4

El operador typeof ...

typeof(T) 

... no va a funcionar con la declaración de C# interruptor. Pero ¿qué tal esto? El siguiente post contiene una clase estática ...

Is there a better alternative than this to 'switch on type'?

... que le permitirá escribir código como este:

TypeSwitch.Do(
    sender, 
    TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"), 
    TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked), 
    TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over")); 
+0

Ver también la respuesta de JaredPar aquí. –

11

Puede usar typeof(T).

private static string BuildClause<T>(IList<T> clause) 
{ 
    Type itemType = typeof(T); 
    if(itemType == typeof(int) || itemType == typeof(decimal)) 
    ... 
} 
1

Usted puede hacer typeOf(T), pero yo revise su método y asegurarse de que su única responsabilidad no violar aquí. Esto sería un olor a código, y eso no quiere decir que no deba hacerse, sino que debe tener cuidado.

El objetivo de los genéricos es poder crear algorthims de tipo agnóstico si no le importa cuál es el tipo o si se ajusta a un cierto conjunto de criterios. Su implementación no es muy genérica.

1

No hay forma de usar la instrucción switch para lo que quiere que haga. La declaración de cambio debe suministrarse con tipos integrales, que no incluyen tipos complejos como un objeto "Tipo" o cualquier otro tipo de objeto para el caso.

2

Su construcción derrota por completo el propósito de un método genérico. Es feo a propósito porque debe haber una manera mejor de lograr lo que estás tratando de lograr, aunque no nos has dado suficiente información para descubrir qué es eso.

6

Por defecto sabemos que no hay una gran manera. Hace un tiempo me sentí frustrado con esto y escribí una pequeña clase de utilidad que me ayudó un poco e hizo la sintaxis un poco más limpia.En esencia se convierte el código en

TypeSwitcher.Do(clause[0], 
    TypeSwitch.Case<int>(x => ...), // x is an int 
    TypeSwitch.Case<decimal>(d => ...), // d is a decimal 
    TypeSwitch.Case<string>(s => ...)); // s is a string 

post completo blog y detalles sobre la aplicación están disponibles aquí

2

Para todos que dice comprobación de tipos y hacer algo basado en el tipo no es una gran idea para los genéricos, estoy de acuerdo, pero creo que podría haber algunas circunstancias en las que esto tenga sentido.

Por ejemplo, si tiene una clase que dicen que está implementada de esa manera (Nota: no estoy mostrando todo lo que hace este código por simplicidad y simplemente lo corté y pegué aquí para que no se construya o funcione como el código completo lo hace, pero se pone el punto a través Asimismo, la Unidad es una enumeración):.

public class FoodCount<TValue> : BaseFoodCount 
{ 
    public TValue Value { get; set; } 

    public override string ToString() 
    { 
     if (Value is decimal) 
     { 
      // Code not cleaned up yet 
      // Some code and values defined in base class 

      mstrValue = Value.ToString(); 
      decimal mdecValue; 
      decimal.TryParse(mstrValue, out mdecValue); 

      mstrValue = decimal.Round(mdecValue).ToString(); 

      mstrValue = mstrValue + mstrUnitOfMeasurement; 
      return mstrValue; 
     } 
     else 
     { 
      // Simply return a string 
      string str = Value.ToString() + mstrUnitOfMeasurement; 
      return str; 
     } 
    } 
} 

...

public class SaturatedFat : FoodCountWithDailyValue<decimal> 
{ 
    public SaturatedFat() 
    { 
     mUnit = Unit.g; 
    } 

} 

public class Fiber : FoodCount<int> 
{ 
    public Fiber() 
    { 
     mUnit = Unit.g; 
    } 
} 

public void DoSomething() 
{ 
     nutritionFields.SaturatedFat oSatFat = new nutritionFields.SaturatedFat(); 

     string mstrValueToDisplayPreFormatted= oSatFat.ToString(); 
} 

Así que en resumen, creo que hay razones válidas por las que es posible que desee compruebe para ver qué tipo es el genérico, para hacer algo especial.

0

¿Qué tal esto:

  // Checks to see if the value passed is valid. 
      if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value)) 
      { 
       throw new ArgumentException(); 
      }