Appel d'une méthode statique sur un paramètre de type générique

107

J'espérais faire quelque chose comme ça, mais cela semble être illégal en C #:


public Collection MethodThatFetchesSomething<T>()
    where T : SomeBaseClass
{
    return T.StaticMethodOnSomeBaseClassThatReturnsCollection();
}

J'obtiens une erreur de compilation: "" T "est un" paramètre de type ", qui n'est pas valide dans le contexte donné."

Étant donné un paramètre de type générique, comment puis-je appeler une méthode statique sur la classe générique? La méthode statique doit être disponible, compte tenu de la contrainte.

Remi Despres-Smyth
la source

Réponses:

59

Dans ce cas, vous devez simplement appeler la méthode statique directement sur le type contraint. C # (et le CLR) ne prennent pas en charge les méthodes statiques virtuelles. Alors:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

... ne peut être différent de:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

Passer par le paramètre de type générique est une indirection inutile et n'est donc pas prise en charge.

JaredPar
la source
25
Mais que faire si vous masquiez votre méthode statique dans une classe enfant? public class SomeChildClass: SomeBaseClass {public new static StaticMethodOnSomeBaseClassThatReturnsCollection () {}} Pourriez-vous faire quelque chose pour accéder à cette méthode statique à partir d'un type générique?
Hugo Migneron
2
Consultez la réponse de Joshua Pech ci-dessous, je pense que cela fonctionnerait dans ce cas.
Remi Despres-Smyth
1
Serait return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();travailler? Si tel est le cas, vous voudrez peut-être ajouter cela à votre réponse. Merci. Cela a fonctionné pour moi. Dans mon cas, ma classe avait un paramètre de type alors je l'ai fait return SomeBaseClass<T>.StaticMethodOnSomeBaseClassThatReturnsCollection();et cela a fonctionné.
toddmo
27

Pour développer une réponse précédente, je pense que la réflexion est plus proche de ce que vous voulez ici. Je pourrais donner 1001 raisons pour lesquelles vous devriez ou ne devriez pas faire quelque chose, je répondrai simplement à votre question telle que vous l'avez posée. Je pense que vous devriez appeler la méthode GetMethod sur le type du paramètre générique et partir de là. Par exemple, pour une fonction:

public void doSomething<T>() where T : someParent
{
    List<T> items=(List<T>)typeof(T).GetMethod("fetchAll").Invoke(null,new object[]{});
    //do something with items
}

Où T est une classe qui a la méthode statique fetchAll ().

Oui, je suis conscient que cela est horriblement lent et peut planter si someParent ne force pas toutes ses classes enfants à implémenter fetchAll mais il répond à la question comme demandé.

Joshua Pech
la source
2
Non pas du tout. JaredPar a tout à fait raison: T.StaticMethodOnSomeBaseClassThatReturnsCollection où T: SomeBaseClass n'est pas différent de simplement déclarer SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection.
Remi Despres-Smyth
2
C'est ce dont j'avais besoin, cela fonctionne avec une méthode statique
myro
C'était la réponse dont j'avais besoin car je n'avais pas le contrôle des classes et de la classe de base.
Tim
8

La seule façon d'appeler une telle méthode serait par réflexion. Cependant, il semble qu'il soit possible d'encapsuler cette fonctionnalité dans une interface et d'utiliser un modèle IoC / factory / etc basé sur une instance.

Marc Gravell
la source
5

Il semble que vous essayez d'utiliser des génériques pour contourner le fait qu'il n'y a pas de «méthodes statiques virtuelles» en C #.

Malheureusement, cela ne fonctionnera pas.

Brad Wilson
la source
1
Je ne suis pas - je travaille au-dessus d'une couche DAL générée. Les classes générées héritent toutes d'une classe de base, qui a une méthode FetchAll statique. J'essaye de réduire la duplication de code dans ma classe de référentiel avec une classe de référentiel "générique" - beaucoup de code répétitif, sauf pour la classe concrète utilisée.
Remi Despres-Smyth
1
Alors pourquoi n'appelez-vous pas simplement SomeBaseClass.StaticMethod ... ()?
Brad Wilson
Désolé, je ne me suis pas bien expliqué dans le commentaire précédent. FetchAll est défini sur la base, mais implémenté sur les classes dérivées. Je dois l'appeler sur la classe dérivée.
Remi Despres-Smyth
7
S'il s'agit d'une méthode statique, elle est à la fois définie et implémentée par la base. Il n'y a pas de méthode statique virtuelle / abstraite en C #, et aucune substitution pour une telle chose. Je soupçonne que vous l'avez simplement re-déclaré, ce qui est très différent.
Marc Gravell
1
Oui, vous avez raison - j'avais fait des hypothèses invalides ici. Merci pour la discussion, cela m'a mis sur la bonne voie.
Remi Despres-Smyth
2

A partir de maintenant, vous ne pouvez pas. Vous avez besoin d'un moyen de dire au compilateur que T a cette méthode, et actuellement, il n'y a aucun moyen de le faire. (Beaucoup poussent Microsoft à étendre ce qui peut être spécifié dans une contrainte générique, donc peut-être que cela sera possible à l'avenir).

James Curran
la source
1
Le problème est que, comme les génériques sont fournis par le runtime, cela signifierait probablement une nouvelle version CLR - qu'ils ont (largement) évitée depuis 2.0. Peut-être que nous en attendons un nouveau, cependant ...
Marc Gravell
2

Ici, je poste un exemple qui fonctionne, c'est une solution de contournement

public interface eInterface {
    void MethodOnSomeBaseClassThatReturnsCollection();
}

public T:SomeBaseClass, eInterface {

   public void MethodOnSomeBaseClassThatReturnsCollection() 
   { StaticMethodOnSomeBaseClassThatReturnsCollection() }

}

public Collection MethodThatFetchesSomething<T>() where T : SomeBaseClass, eInterface
{ 
   return ((eInterface)(new T()).StaticMethodOnSomeBaseClassThatReturnsCollection();
}
rodrijp
la source
2
Cela me donne une erreur de syntaxe? Que veut public T : SomeBaseClassdire?
Eric du
Si votre classe a une méthode d'instance someInstanceMethod (), vous pouvez toujours l'appeler en faisant (new T ()). SomeInstanceMethod (); - mais cela appelle une méthode d'instance - la question demandait comment appeler une méthode statique de la classe.
timothy
2

Je voulais juste dire que parfois les délégués résolvent ces problèmes, selon le contexte.

Si vous devez appeler la méthode statique comme une sorte de fabrique ou de méthode d'initialisation, vous pouvez déclarer un délégué et passer la méthode statique à la fabrique générique appropriée ou à tout ce qui a besoin de cette "classe générique avec cette méthode statique".

Par exemple:

class Factory<TProduct> where TProduct : new()
{
    public delegate void ProductInitializationMethod(TProduct newProduct);


    private ProductInitializationMethod m_ProductInitializationMethod;


    public Factory(ProductInitializationMethod p_ProductInitializationMethod)
    {
        m_ProductInitializationMethod = p_ProductInitializationMethod;
    }

    public TProduct CreateProduct()
    {
        var prod = new TProduct();
        m_ProductInitializationMethod(prod);
        return prod;
    }
}

class ProductA
{
    public static void InitializeProduct(ProductA newProduct)
    {
        // .. Do something with a new ProductA
    }
}

class ProductB
{
    public static void InitializeProduct(ProductB newProduct)
    {
        // .. Do something with a new ProductA
    }
}

class GenericAndDelegateTest
{
    public static void Main()
    {
        var factoryA = new Factory<ProductA>(ProductA.InitializeProduct);
        var factoryB = new Factory<ProductB>(ProductB.InitializeProduct);

        ProductA prodA = factoryA.CreateProduct();
        ProductB prodB = factoryB.CreateProduct();
    }
}

Malheureusement, vous ne pouvez pas garantir que la classe a la bonne méthode, mais vous pouvez au moins faire en sorte que la méthode de fabrique résultante ait tout ce qu'elle attend (c'est-à-dire une méthode d'initialisation avec exactement la bonne signature). C'est mieux qu'une exception de réflexion d'exécution.

Cette approche présente également certains avantages, c'est-à-dire que vous pouvez réutiliser les méthodes d'initialisation, les utiliser comme méthodes d'instance, etc.

Amir Abiri
la source
1

Vous devriez pouvoir le faire en utilisant la réflexion, comme décrit ici

En raison du lien mort, j'ai trouvé les détails pertinents dans la machine de retour:

Supposons que vous ayez une classe avec une méthode générique statique:

class ClassWithGenericStaticMethod
{
    public static void PrintName<T>(string prefix) where T : class
    {
        Console.WriteLine(prefix + " " + typeof(T).FullName);
    }
}

Comment pouvez-vous invoquer cette méthode en utilisant la relection?

Cela s'avère très simple… Voici comment invoquer une méthode générique statique à l'aide de la réflexion:

// Grabbing the type that has the static generic method
Type typeofClassWithGenericStaticMethod = typeof(ClassWithGenericStaticMethod);

// Grabbing the specific static method
MethodInfo methodInfo = typeofClassWithGenericStaticMethod.GetMethod("PrintName", System.Reflection.BindingFlags.Static | BindingFlags.Public);

// Binding the method info to generic arguments
Type[] genericArguments = new Type[] { typeof(Program) };
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);

// Simply invoking the method and passing parameters
// The null parameter is the object to call the method from. Since the method is
// static, pass null.
object returnValue = genericMethodInfo.Invoke(null, new object[] { "hello" });
johnc
la source
Le lien est mort.
Necronomicron le