Aufrufen einer statischen Methode für einen generischen Typparameter

107

Ich hatte gehofft, so etwas zu tun, aber es scheint in C # illegal zu sein:


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

Ich erhalte einen Fehler bei der Kompilierung: "'T' ist ein 'Typparameter', der im angegebenen Kontext nicht gültig ist."

Wie kann ich bei einem generischen Typparameter eine statische Methode für die generische Klasse aufrufen? Die statische Methode muss unter Berücksichtigung der Einschränkung verfügbar sein.

Remi Despres-Smyth
quelle

Antworten:

58

In diesem Fall sollten Sie die statische Methode für den eingeschränkten Typ direkt aufrufen. C # (und die CLR) unterstützen keine virtuellen statischen Methoden. So:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

... kann nicht anders sein als:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

Das Durchlaufen des generischen Typparameters ist eine nicht benötigte Indirektion und wird daher nicht unterstützt.

JaredPar
quelle
24
Aber was ist, wenn Sie Ihre statische Methode in einer untergeordneten Klasse maskiert haben? public class SomeChildClass: SomeBaseClass {public new static StaticMethodOnSomeBaseClassThatReturnsCollection () {}} Könnten Sie etwas tun, um von einem generischen Typ auf diese statische Methode zuzugreifen?
Hugo Migneron
2
Schauen Sie sich die Antwort von Joshua Pech unten an. Ich glaube, das würde in diesem Fall funktionieren.
Remi Despres-Smyth
1
Würde return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();funktionieren? Wenn ja, möchten Sie das vielleicht zu Ihrer Antwort hinzufügen. Vielen Dank. Es hat bei mir funktioniert. In meinem Fall hatte meine Klasse einen Typparameter, also tat ich das return SomeBaseClass<T>.StaticMethodOnSomeBaseClassThatReturnsCollection();und das funktionierte.
Toddmo
27

Um auf eine frühere Antwort einzugehen, denke ich, dass die Reflexion näher an dem liegt, was Sie hier wollen. Ich könnte 1001 Gründe nennen, warum Sie etwas tun sollten oder nicht, ich beantworte Ihre Frage einfach wie gestellt. Ich denke, Sie sollten die GetMethod-Methode für den Typ des generischen Parameters aufrufen und von dort aus fortfahren. Zum Beispiel für eine Funktion:

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

Dabei ist T eine Klasse mit der statischen Methode fetchAll ().

Ja, ich bin mir bewusst, dass dies schrecklich langsam ist und abstürzen kann, wenn someParent nicht alle untergeordneten Klassen zwingt, fetchAll zu implementieren, aber die gestellte Frage beantwortet.

Joshua Pech
quelle
2
Nein überhaupt nicht. JaredPar hat es absolut richtig gemacht: T.StaticMethodOnSomeBaseClassThatReturnsCollection wobei T: SomeBaseClass sich nicht von der einfachen Angabe von SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection unterscheidet.
Remi Despres-Smyth
2
Dies ist, was ich brauchte, es funktioniert mit einer statischen Methode
Myro
Dies war die Antwort, die ich brauchte, weil ich keine Kontrolle über die Klassen und die Basisklasse hatte.
Tim
8

Die einzige Möglichkeit, eine solche Methode aufzurufen, wäre die Reflexion. Es scheint jedoch möglich zu sein, diese Funktionalität in eine Schnittstelle zu packen und ein instanzbasiertes IoC / factory / etc-Muster zu verwenden.

Marc Gravell
quelle
5

Es hört sich so an, als würden Sie versuchen, mithilfe von Generika die Tatsache zu umgehen, dass es in C # keine "virtuellen statischen Methoden" gibt.

Leider wird das nicht funktionieren.

Brad Wilson
quelle
1
Ich arbeite nicht über einer generierten DAL-Ebene. Die generierten Klassen erben alle von einer Basisklasse, die über eine statische FetchAll-Methode verfügt. Ich versuche, die Codeduplizierung in meiner Repository-Klasse durch eine "generische" Repository-Klasse zu reduzieren - viel sich wiederholender Code, mit Ausnahme der verwendeten konkreten Klasse.
Remi Despres-Smyth
1
Warum rufen Sie dann nicht einfach SomeBaseClass.StaticMethod ... () auf?
Brad Wilson
Entschuldigung, ich habe mich im vorherigen Kommentar nicht gut erklärt. FetchAll wird auf der Basis definiert, aber auf den abgeleiteten Klassen implementiert. Ich muss es für die abgeleitete Klasse aufrufen.
Remi Despres-Smyth
7
Wenn es sich um eine statische Methode handelt, wird sie von der Basis definiert und implementiert. In C # gibt es keine virtuelle / abstrakte statische Methode und keine Überschreibung für diese. Ich vermute, Sie haben es einfach neu deklariert, was sehr unterschiedlich ist.
Marc Gravell
1
Ja, Sie haben Recht - ich hatte hier ungültige Annahmen getroffen. Vielen Dank für die Diskussion, es hat mir geholfen, auf den richtigen Weg zu kommen.
Remi Despres-Smyth
2

Ab sofort kannst du nicht. Sie müssen dem Compiler mitteilen, dass T über diese Methode verfügt, und derzeit gibt es keine Möglichkeit, dies zu tun. (Viele drängen Microsoft, das zu erweitern, was in einer generischen Einschränkung angegeben werden kann, sodass dies möglicherweise in Zukunft möglich sein wird.)

James Curran
quelle
1
Das Problem ist, dass, da Generika von der Laufzeit bereitgestellt werden, dies wahrscheinlich eine neue CLR-Version bedeuten würde - die sie (weitgehend) seit 2.0 vermieden haben. Vielleicht ist aber eine neue fällig ...
Marc Gravell
2

Hier poste ich ein Beispiel, das funktioniert, es ist eine Problemumgehung

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
quelle
2
Dies gibt einen Syntaxfehler für mich? Was heißt public T : SomeBaseClassdas
Eric
Wenn Ihre Klasse eine Instanzmethode someInstanceMethod () hat, können Sie diese immer aufrufen, indem Sie (new T ()). SomeInstanceMethod (); - aber dies ruft eine Instanzmethode auf - die Frage, wie eine statische Methode der Klasse aufgerufen werden soll.
Timothy
2

Ich wollte es nur rauswerfen, dass Delegierte diese Probleme manchmal lösen, je nach Kontext.

Wenn Sie die statische Methode als eine Art Factory- oder Initialisierungsmethode aufrufen müssen, können Sie einen Delegaten deklarieren und die statische Methode an die entsprechende generische Factory übergeben oder was auch immer diese "generische Klasse mit dieser statischen Methode" benötigt.

Beispielsweise:

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

Leider können Sie nicht erzwingen, dass die Klasse die richtige Methode hat, aber Sie können zumindest beim Kompilieren erzwingen, dass die resultierende Factory-Methode alles hat, was sie erwartet (dh eine Initialisierungsmethode mit genau der richtigen Signatur). Dies ist besser als eine Laufzeitreflexionsausnahme.

Dieser Ansatz hat auch einige Vorteile, dh Sie können Init-Methoden wiederverwenden, Instanzmethoden sein usw.

Amir Abiri
quelle
1

Sie sollten diese mit Reflexion tun können, wie beschrieben hier

Da der Link tot ist, habe ich die relevanten Details in der Wayback-Maschine gefunden:

Angenommen, Sie haben eine Klasse mit einer statischen generischen Methode:

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

Wie können Sie diese Methode mit relection aufrufen?

Es stellt sich als sehr einfach heraus… So rufen Sie eine statische generische Methode mit Reflection auf:

// 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
quelle
Der Link ist tot.
Necronomicron