2012-08-23 8 views
5

Me encuentro con un problema de genéricos mientras trabajo en un controlador genérico de Inyección de Dependencia (un localizador de servicios básico).¿Cómo se puede convertir T en una clase para que coincida con una restricción "donde T: clase"?

Edición 1 (para mayor claridad)

Bueno por lo que en realidad estoy usando SimpleInjector como un dispositivo de resolución DI y tiene la restricción de clase en su método GetInstance, asi que aquí hay algo más de código completo:

public T GetInstance<T>() where T : class 
    { 
    try 
    { 
     // works 
     return _container.GetInstance<T>(); 
    } 
    catch(ActivationException aEx) 
    { 
     return default(T); 
    } 
    } 

    public T GetInstance<T>() 
    { 
    try 
    { 
     if(typeof(T).IsClass) 
     { 
      // does not work, T is not a reference type 
      return _container.GetInstance<T>(); 
     } 
    } 
    catch(ActivationException aEx) 
    { 
     return default(T); 
    } 
    } 

Editar 2 - código final, ya que tiene un aspecto extraño en los comentarios:

public T GetInstance<T>() 
    { 
    try 
    { 
     if(typeof(T).IsClass) 
     { 
      return (T) _container.GetInstance(typeof(T)); 
     } 
    } 
    catch(ActivationException aEx) 
    { 
     return default(T); 
    } 
    } 
+5

Tal vez si supiéramos más acerca de por qué usted está interesado en hacer esto, aparte de un ejemplo nula regresar ...? Podría ser una mejor forma de estructurarlo. –

+0

Consulte la edición 1 de por qué quiero hacer esto. – Laurence

Respuesta

1

¿Puede utilizar un método más de ayuda? Por favor, encontrar la clase de prueba a continuación

public class Test 
{ 
    public T GetInstance<T>() where T : class 
    { 
    return (T)GetInstance(typeof(T)); 
    } 

    private object GetInstance(Type type) 
    { 
    return Activator.CreateInstance(type); 
    } 

    public T GetEvenMoreGenericInstance<T>() 
    { 
    if(!typeof(T).IsValueType) 
    { 
     return (T)GetInstance(typeof(T)); 
    } 
    return default(T); 
    } 
} 
+0

Eso realmente funciona, gracias. Me había olvidado de fundición a T: 'T pública GetInstance () { tratar { si (typeof (T) .IsClass) { de retorno (T) _container.GetInstance (typeof (T)); } } catch (ActivationException aEx) { return default (T); } } ' – Laurence

1

La respuesta corta es que no puede hacer esto, al menos no directamente. El compilador tendría que hacer un montón de trabajo para garantizar que T siempre será una clase en su situación, por lo que no le permitirá pasarlo como un parámetro de tipo genérico sin aplicar la misma restricción de tipo a GetEvenMoreGenericInstance.

Puede lograr esto a través de la reflexión, o crear una sobrecarga no genérica de GetInstance que toma un tipo como parámetro. Recomendaría la opción de parámetro Tipo, o reestructurar su código por completo para eliminar la necesidad de llamar a este método.

0

Se podría utilizar la reflexión para encontrar una variante de GetInstance, a continuación, llamar a la apropiada.

el siguiente ejemplo se realiza llamadas a métodos estáticos, pero podría extenderla:

namespace Scratch 
{ 
    internal class Foo 
    { 
     // A class to create 
    } 

    class Program 
    { 
     public static T GetInstance<T>() where T : class, new() 
     { 
      return new T(); // Or whatever... 
     } 

     public static T CallGeneric<T>(Func<object> f) 
     { 
      var method = f.Method; 

      var converted = method.GetGenericMethodDefinition().MakeGenericMethod(typeof(T)); 

      return (T) converted.Invoke(null, new object[] {}); 
     } 

     public static T GetEvenMoreGenericInstance<T>() 
     { 
      if(!typeof(T).IsValueType) 
      { 
       return CallGeneric<T>(GetInstance<object>); 
      } 
      return default(T); 
     } 

     static void Main(string[] args) 
     { 
      var a = GetEvenMoreGenericInstance<int>(); 
      var b = GetEvenMoreGenericInstance<Foo>(); 
     } 
    } 
} 
+0

Me gusta el uso de la reflexión :) – Laurence

Cuestiones relacionadas