2009-09-14 30 views
5

estoy implementando una interfaz genérica (iqueryprovider, específicamente). en algún momento, me veo obligado a devolver un resultado genérico, lo que necesito para llegar de alguna de las interfaces internas:restricciones variables de tipo genérico e interfaces

public TResult Execute<TResult>(...) { 
    return something.Foo<TResult>(); 
} 

donde something.Foo es

public T Foo<T>() where T: MyBaseClass, new() { 
    ... 
} 

esto por supuesto hace saltar porque el TResult definido externamente no tiene las mismas restricciones de tipo que la pregunta de T. internamente definida: ¿hay alguna manera de hacer que TResult sea aceptable para Foo? ¿Puedo de alguna manera probar de forma explícita esas dos condiciones y forzar la variable de tipo?

Respuesta

2

usted podría intentar algo como esto:

public TResult Execute<TResult>(...) 
{ 
    if (typeof(TResult) is MyBaseClass) 
    { 
     Type mytype = typeof(TResult); 
     MethodInfo method = typeof({TypewhereFoo<>IsDeclared}).GetMethod("Foo"); 
     MethodInfo generic = method.MakeGenericMethod(myType); 
     return (TResult)generic.Invoke(this, null); 
    } 
    else 
    { 
    // Throw here 
    } 
} 
+2

* suspiro * mientras que eso funciona, eso es muy desagradable, especialmente en el código de nivel de marco. – kolosy

+0

terminé yendo por este camino. No soy partidario de la suave dependencia del método, pero supera las restricciones y sigue haciendo llamadas de reflexión sobre algunas de las otras cosas que hizo el método interno. – kolosy

0

Usted tendrá que añadir las restricciones de tipo a su método genérico:

public TResult Execute<TResult>(...) where TResult: MyBaseClass, new() { 
    return something.Foo<TResult>(); 
} 
+1

eso no funciona, porque esa firma de método se define en una interfaz que no controlo. agregar esas restricciones hace que ese método sea una sobrecarga para la implementación de la interfaz. – kolosy

1

Nop. Si TResult no tiene restricciones, puede ser cualquier cosa. Si su método de ayuda no puede llevar nada viejo, necesitará un mejor método de ayuda. La interfaz requiere que brindes más servicios de los que tu ayudante puede brindarte, por lo tanto, tendrás que hacer el trabajo para proporcionar ese servicio.

0

Ay ... tiene un problema. No hay forma de llamar a algo.Foo() ya que no tiene un tipo que sea compatible. Se podría 'piratear' evitar esto mediante la creación de un tipo 'envoltorio' que es compatible para llamar al Foo() y luego 'Separar':

class MyNastyFix<T> : MyBaseClass 
    { 
     public T Unwrap() 
     { 
      //assert that T has the correct base type 
      if (!typeof(T).IsSubclassOf(typeof(MyBaseClass))) 
       throw new ArgumentException(); 
      //must use reflection to construct 
      T obj = (T)typeof(T).InvokeMember(null, BindingFlags.CreateInstance, null, null, null); 
      //cast to a type of MyBaseClass so we can copy our values 
      MyBaseClass c = (MyBaseClass)(object)obj; 
      c.SomeValue = this.SomeValue; 
      return obj; 
     } 
    } 

    public static TResult Execute<TResult>() 
    { 
     return something.Foo<MyNastyFix<TResult>>().Unwrap(); 
    } 

Actualización: La respuesta de reflexión podría ser un enfoque mejor si funciona.

+0

yup, eso es una variación de la otra respuesta de reflexión ... – kolosy

0

Cambio Foo para comprobar las limitaciones en tiempo de ejecución:

public T Foo<T>() { 
    if (!typeof(T).IsAssignableFrom(typeof(MyBaseClass)) 
     || !typeof(T).GetConstructor(...)) 
     throw new System.NotImplementedException(); 
    ... 
} 

Las limitaciones genéricas se comprueban en tiempo de compilación para que no puedan basarse en las condiciones de tiempo de ejecución.

Cuestiones relacionadas