Memanggil metode statis pada parameter tipe generik

107

Saya berharap untuk melakukan sesuatu seperti ini, tetapi tampaknya ilegal di C #:


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

Saya mendapatkan kesalahan waktu kompilasi: "'T' adalah 'parameter tipe', yang tidak valid dalam konteks yang diberikan."

Diberikan parameter tipe generik, bagaimana saya dapat memanggil metode statis pada kelas generik? Metode statis harus tersedia, mengingat batasannya.

Remi Despres-Smyth
sumber

Jawaban:

59

Dalam hal ini Anda hanya perlu memanggil metode statis pada tipe yang dibatasi secara langsung. C # (dan CLR) tidak mendukung metode statis virtual. Begitu:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

... tidak bisa berbeda dengan:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

Melalui parameter tipe generik adalah tipuan yang tidak dibutuhkan dan karenanya tidak didukung.

JaredPar
sumber
25
Tetapi bagaimana jika Anda menutupi metode statis Anda di kelas anak? public class SomeChildClass: SomeBaseClass {public static StaticMethodOnSomeBaseClassThatReturnsCollection () {}} Bisakah Anda melakukan sesuatu untuk mengakses metode statis dari tipe generik?
Hugo Migneron
2
Lihat jawaban Joshua Pech di bawah ini, saya yakin itu akan berhasil dalam kasus itu.
Remi Despres-Smyth
1
Akan return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();berhasil? Jika demikian, Anda mungkin ingin menambahkannya ke jawaban Anda. Terima kasih. Itu berhasil untuk saya. Dalam kasus saya, kelas saya memiliki tipe param jadi saya lakukan return SomeBaseClass<T>.StaticMethodOnSomeBaseClassThatReturnsCollection();dan itu berhasil.
toddmo
27

Untuk menguraikan jawaban sebelumnya, menurut saya refleksi lebih dekat dengan apa yang Anda inginkan di sini. Saya dapat memberikan 1001 alasan mengapa Anda harus atau tidak harus melakukan sesuatu, saya akan menjawab pertanyaan Anda seperti yang ditanyakan. Saya pikir Anda harus memanggil metode GetMethod pada jenis parameter generik dan pergi dari sana. Misalnya, untuk suatu fungsi:

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

Di mana T adalah kelas apa pun yang memiliki metode statis fetchAll ().

Ya, saya sadar ini sangat lambat dan mungkin macet jika someParent tidak memaksa semua kelas turunannya untuk mengimplementasikan fetchAll tetapi menjawab pertanyaan seperti yang ditanyakan.

Joshua Pech
sumber
2
Tidak, tidak sama sekali. JaredPar benar: T.StaticMethodOnSomeBaseClassThatReturnsCollection di mana T: SomeBaseClass tidak berbeda dengan hanya menyatakan SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection.
Remi Despres-Smyth
2
Inilah yang saya butuhkan, ini berfungsi dengan metode statis
myro
Ini adalah jawaban yang saya butuhkan karena saya tidak memiliki kendali atas kelas dan kelas dasar.
Tim
8

Satu-satunya cara untuk memanggil metode semacam itu adalah melalui refleksi, Namun, sepertinya dimungkinkan untuk menggabungkan fungsionalitas itu dalam antarmuka dan menggunakan pola IoC / pabrik / dll berbasis instans.

Marc Gravell
sumber
5

Sepertinya Anda mencoba menggunakan obat generik untuk mengatasi fakta bahwa tidak ada "metode statis virtual" di C #.

Sayangnya, itu tidak akan berhasil.

Brad Wilson
sumber
1
Saya tidak - Saya bekerja di atas lapisan DAL yang dihasilkan. Semua kelas yang dihasilkan mewarisi dari kelas dasar, yang memiliki metode FetchAll statis. Saya mencoba untuk mengurangi duplikasi kode di kelas repositori saya dengan kelas repositori "generik" - banyak kode berulang, kecuali untuk kelas beton yang digunakan.
Remi Despres-Smyth
1
Lalu mengapa Anda tidak memanggil SomeBaseClass.StaticMethod ... ()?
Brad Wilson
Maaf, saya tidak menjelaskan diri saya dengan baik di komentar sebelumnya. FetchAll didefinisikan di basis, tetapi diimplementasikan pada kelas turunan. Saya perlu menyebutnya di kelas turunan.
Remi Despres-Smyth
7
Jika ini adalah metode statis, maka itu ditentukan dan diterapkan oleh basis. Tidak ada metode statis virtual / abstrak di C #, dan tidak ada penggantian untuk metode tersebut. Saya menduga Anda baru saja menyatakannya kembali, yang sangat berbeda.
Marc Gravell
1
Ya, Anda benar - saya telah membuat asumsi yang tidak valid di sini. Terima kasih atas pembahasannya, ini membantu menempatkan saya di jalur yang benar.
Remi Despres-Smyth
2

Sampai sekarang, Anda tidak bisa. Anda memerlukan cara untuk memberi tahu kompilator bahwa T memiliki metode itu, dan saat ini, tidak ada cara untuk melakukannya. (Banyak yang mendorong Microsoft untuk memperluas apa yang dapat ditentukan dalam batasan umum, jadi mungkin ini akan memungkinkan di masa mendatang).

James Curran
sumber
1
Masalahnya adalah karena obat generik disediakan oleh runtime, ini mungkin berarti versi CLR baru - yang (sebagian besar) mereka hindari sejak 2.0. Mungkin kita akan mendapatkan yang baru, meskipun ...
Marc Gravell
2

Di sini, saya memposting contoh yang berhasil, itu solusinya

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
sumber
2
Ini memberikan kesalahan sintaks untuk saya? Apa public T : SomeBaseClassmaksudnya
Eric
Jika kelas Anda memiliki metode instance someInstanceMethod (), Anda selalu bisa memanggilnya dengan melakukan (new T ()). SomeInstanceMethod (); - tapi ini memanggil metode instan - pertanyaannya menanyakan bagaimana memanggil metode statis kelas.
timothy
2

Saya hanya ingin menunjukkan bahwa terkadang delegasi memecahkan masalah ini, tergantung pada konteksnya.

Jika Anda perlu memanggil metode statis sebagai semacam pabrik atau metode inisialisasi, Anda dapat mendeklarasikan delegasi dan meneruskan metode statis ke pabrik generik yang relevan atau apa pun yang membutuhkan "kelas generik dengan metode statis ini".

Sebagai contoh:

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

Sayangnya Anda tidak dapat memaksakan bahwa kelas memiliki metode yang tepat, tetapi Anda setidaknya dapat mengkompilasi-waktu-menegakkan bahwa metode pabrik yang dihasilkan memiliki semua yang diharapkan (yaitu metode inisialisasi dengan tanda tangan yang tepat). Ini lebih baik daripada pengecualian refleksi waktu proses.

Pendekatan ini juga memiliki beberapa keuntungan, yaitu Anda dapat menggunakan kembali metode init, menjadikannya metode instance, dll.

Amir Abiri
sumber
1

Anda harus dapat melakukan ini dengan menggunakan refleksi, seperti yang dijelaskan di sini

Karena tautan mati, saya menemukan detail yang relevan di mesin wayback:

Asumsikan Anda memiliki kelas dengan metode generik statis:

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

Bagaimana Anda bisa menggunakan metode ini menggunakan releksi?

Ternyata sangat mudah… Beginilah cara Anda Memanggil Metode Generik Statis menggunakan Refleksi:

// 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
sumber
Tautannya sudah mati.
Necronomicron