web-dev-qa-db-fra.com

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

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 au moment de la 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.

97
Remi Despres-Smyth

Dans ce cas, vous devez simplement appeler directement la méthode statique 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 donc non prise en charge.

53
JaredPar

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 comme demandé. 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 n'importe quelle classe qui a la méthode statique fetchAll ().

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

25
Joshua Pech

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

7
Marc Gravell

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, ça ne marchera pas.

5
Brad Wilson

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

À 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 à développer ce qui peut être spécifié dans une contrainte générique, donc cela sera peut-être possible à l'avenir).

2
James Curran

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

Si vous avez besoin d'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 transmettre 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 imposer que la classe a la bonne méthode, mais vous pouvez au moins compiler au moment de la compilation que la méthode d'usine résultante a 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 à l'exécution.

Cette approche présente également certains avantages, c'est-à-dire que vous pouvez réutiliser les méthodes init, qu'elles soient des méthodes d'instance, etc.

2
Amir Abiri

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

1
johnc