Chiamata a un metodo statico su un parametro di tipo generico


107

Speravo di fare qualcosa di simile, ma sembra essere illegale in C #:


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

Ottengo un errore in fase di compilazione: "'T' è un 'parametro di tipo', che non è valido nel contesto dato."

Dato un parametro di tipo generico, come posso chiamare un metodo statico sulla classe generica? Il metodo statico deve essere disponibile, dato il vincolo.


Risposte:


59

In questo caso dovresti semplicemente chiamare direttamente il metodo statico sul tipo vincolato. C # (e CLR) non supportano i metodi statici virtuali. Così:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

... non può essere diverso da:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

Passare attraverso il parametro di tipo generico è un riferimento indiretto non necessario e quindi non supportato.


25
Ma cosa succede se mascherassi il tuo metodo statico in una classe figlio? public class SomeChildClass: SomeBaseClass {public new static StaticMethodOnSomeBaseClassThatReturnsCollection () {}} Potresti fare qualcosa per accedere a quel metodo statico da un tipo generico?
Hugo Migneron

2
Dai un'occhiata alla risposta di Joshua Pech di seguito, credo che funzionerebbe in quel caso.
Remi Despres-Smyth

1
Funzionerebbe return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();? Se è così potresti aggiungerlo alla tua risposta. Grazie. Ha funzionato per me. Nel mio caso la mia classe aveva un parametro di tipo, quindi l'ho fatto return SomeBaseClass<T>.StaticMethodOnSomeBaseClassThatReturnsCollection();e ha funzionato.
Toddmo

27

Per elaborare una risposta precedente, penso che la riflessione sia più vicina a ciò che vuoi qui. Potrei dare 1001 motivi per cui dovresti o non dovresti fare qualcosa, risponderò alla tua domanda come richiesto. Penso che dovresti chiamare il metodo GetMethod sul tipo di parametro generico e andare da lì. Ad esempio, per una funzione:

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

Dove T è qualsiasi classe che ha il metodo statico fetchAll ().

Sì, sono consapevole che questo è orribilmente lento e potrebbe bloccarsi se someParent non forza tutte le sue classi figlie a implementare fetchAll ma risponde alla domanda come chiesto.


2
No, per niente. JaredPar ha perfettamente ragione: T.StaticMethodOnSomeBaseClassThatReturnsCollection dove T: SomeBaseClass non è diverso dal semplice affermare SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection.
Remi Despres-Smyth

2
Questo è ciò di cui avevo bisogno, funziona con un metodo statico
myro

Questa era la risposta di cui avevo bisogno perché non avevo il controllo delle classi e della classe base.
Tim,

8

L'unico modo per chiamare un tale metodo sarebbe tramite la riflessione, tuttavia, sembra che potrebbe essere possibile racchiudere quella funzionalità in un'interfaccia e utilizzare un pattern IoC / factory / etc basato sull'istanza.


5

Sembra che tu stia cercando di utilizzare i generici per aggirare il fatto che non esistono "metodi statici virtuali" in C #.

Sfortunatamente, non funzionerà.


1
Non sto lavorando sopra un livello DAL generato. Le classi generate ereditano tutte da una classe base, che ha un metodo FetchAll statico. Sto cercando di ridurre la duplicazione del codice nella mia classe di repository con una classe di repository "generica" ​​- un sacco di codice ripetuto, ad eccezione della classe concreta utilizzata.
Remi Despres-Smyth

1
Allora perché non chiami semplicemente SomeBaseClass.StaticMethod ... ()?
Brad Wilson

Scusa, non mi sono spiegato bene nel commento precedente. FetchAll è definito sulla base, ma implementato sulle classi derivate. Devo chiamarlo sulla classe derivata.
Remi Despres-Smyth

7
Se è un metodo statico, allora è sia definito che implementato dalla base. Non esiste un metodo statico virtuale / astratto in C # e nessun override per tale. Sospetto che tu l'abbia semplicemente ribadito, il che è molto diverso.
Marc Gravell

1
Sì, hai ragione - avevo fatto ipotesi non valide qui. Grazie per la discussione, mi ha aiutato a mettermi sulla strada giusta.
Remi Despres-Smyth

2

Per ora non puoi. Hai bisogno di un modo per dire al compilatore che T ha quel metodo e, al momento, non c'è modo di farlo. (Molti stanno spingendo Microsoft ad espandere ciò che può essere specificato in un vincolo generico, quindi forse questo sarà possibile in futuro).


1
Il problema è che poiché i generici sono forniti dal runtime, ciò significherebbe probabilmente una nuova versione CLR, che hanno (in gran parte) evitato dalla 2.0. Forse
ce ne dovrebbe

2

Qui, inserisco un esempio che funziona, è una soluzione alternativa

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
Questo dà un errore di sintassi per me? Cosa public T : SomeBaseClasssignifica?
Eric,

Se la tua classe ha un metodo di istanza someInstanceMethod () puoi sempre chiamarlo facendo (new T ()). SomeInstanceMethod (); - ma questo sta chiamando un metodo di istanza - la domanda ha chiesto come chiamare un metodo statico della classe.
timothy

2

Volevo solo dire che a volte i delegati risolvono questi problemi, a seconda del contesto.

Se è necessario chiamare il metodo statico come una sorta di metodo factory o di inizializzazione, è possibile dichiarare un delegato e passare il metodo statico alla factory generica pertinente o qualsiasi altra cosa richieda questa "classe generica con questo metodo statico".

Per esempio:

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

Sfortunatamente non puoi imporre che la classe abbia il metodo giusto, ma puoi almeno in fase di compilazione-imporre che il metodo factory risultante abbia tutto ciò che si aspetta (cioè un metodo di inizializzazione con esattamente la firma giusta). È meglio di un'eccezione di riflessione in fase di esecuzione.

Questo approccio ha anche alcuni vantaggi, ovvero puoi riutilizzare i metodi di inizializzazione, farli essere metodi di istanza, ecc.


1

Dovresti essere in grado di farlo usando la riflessione, come descritto qui

Dato che il link è morto, ho trovato i dettagli rilevanti nella macchina di ritorno:

Supponi di avere una classe con un metodo generico statico:

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

Come puoi invocare questo metodo usando la relection?

Risulta essere molto semplice ... Ecco come invocare un metodo generico statico utilizzando la riflessione:

// 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" });

Il collegamento è morto.
Necronomicron
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.