2009-09-14 10 views
5

J'implémente une interface générique (iqueryprovider, spécifiquement). à un moment donné, je suis obligé de retourner un résultat générique, que je dois obtenir de une interface interne:restrictions et interfaces variables de type générique

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

où something.Foo est

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

Bien sûr, cela explose parce que la TResult défini de l'extérieur n'a pas les mêmes restrictions de type que le T. défini en interne la question: est-il un moyen de rendre TResult acceptable à Foo? puis-je tester de façon explicite pour ces deux conditions et contraindre la variable de type?

Répondre

2

Vous pouvez essayer quelque chose comme ceci:

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

* soupir * alors que ça marche, c'est sacrément méchant, surtout dans le code au niveau du framework. – kolosy

+0

J'ai fini par aller de cette façon. Je ne suis pas un fan de la dépendance de la méthode, mais il bat les restrictions, et continue de faire des réflexions sur les autres choses que la méthode interne a fait. – kolosy

0

Vous devrez ajouter les autres types de restrictions à votre méthode générique:

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

qui ne fonctionne pas, car cette signature de méthode est définie sur une interface que je ne contrôle pas. l'ajout de ces restrictions fait de cette méthode une surcharge de l'implémentation de l'interface. – kolosy

1

Non. Si TResult n'a pas de contraintes, alors cela peut être n'importe quoi. Si votre méthode d'assistance ne peut pas prendre de vieilles choses, alors vous aurez besoin d'une meilleure méthode d'assistance. L'interface vous oblige à fournir plus de services que votre assistant peut fournir, par conséquent, vous allez devoir faire le travail pour fournir ce service.

0

Aïe ... vous avez un problème. Il n'y a aucun moyen d'appeler quelque chose.Foo() puisque vous n'avez pas de type compatible. Vous pouvez « pirater » autour de cela en créant un type « wrapper » qui est compatible pour appeler le Foo() puis « Déballer »:

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

Mise à jour: La réponse de réflexion pourrait être une meilleure approche si cela fonctionne.

+0

yup, c'est une variation de l'autre réponse de réflexion ... – kolosy

0

changement Foo pour vérifier les contraintes lors de l'exécution:

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

Les contraintes génériques sont vérifiées à la compilation afin qu'ils ne peuvent pas être basées sur les conditions d'exécution.

Questions connexes