Genel bir tür parametresinde statik bir yöntemi çağırma


107

Böyle bir şey yapmayı umuyordum, ancak C # 'da yasa dışı gibi görünüyor:


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

Derleme zamanı hatası alıyorum: "'T', verilen bağlamda geçerli olmayan bir 'tür parametresidir."

Genel bir tür parametresi verildiğinde, genel sınıfta statik bir yöntemi nasıl çağırabilirim? Kısıtlama göz önüne alındığında statik metot mevcut olmalıdır.


Yanıtlar:


59

Bu durumda, sadece kısıtlı tipte statik yöntemi doğrudan çağırmalısınız. C # (ve CLR) sanal statik yöntemleri desteklemez. Yani:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

... şundan farklı olamaz:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

Genel tür parametresinden geçmek gereksiz bir yönlendirmedir ve bu nedenle desteklenmez.


25
Peki ya statik yönteminizi bir alt sınıfta maskelerseniz? public class SomeChildClass: SomeBaseClass {public new static StaticMethodOnSomeBaseClassThatReturnsCollection () {}} Bu statik yönteme genel bir türden erişmek için bir şeyler yapabilir misiniz?
Hugo Migneron

2
Joshua Pech'in aşağıdaki cevabına bakın, bu durumda işe yarayacağına inanıyorum.
Remi Despres-Smyth

1
Çalışır mıydı return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();? Eğer öyleyse bunu cevabınıza eklemek isteyebilirsiniz. Teşekkürler. Benim için çalıştı. Benim durumumda sınıfımın bir tür parametresi vardı, bu yüzden yaptım return SomeBaseClass<T>.StaticMethodOnSomeBaseClassThatReturnsCollection();ve işe yaradı.
Toddmo

27

Önceki bir cevabı detaylandırmak için, burada düşünmek istediğiniz şeye daha yakın olduğunu düşünüyorum. Bir şeyi neden yapmanız veya yapmamanız gerektiğine dair 1001 neden verebilirim, sorduğunuz soruyu cevaplayacağım. Bence jenerik parametrenin türü üzerinde GetMethod yöntemini çağırmalı ve oradan gitmelisiniz. Örneğin, bir işlev için:

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

T, fetchAll () statik yöntemine sahip herhangi bir sınıftır.

Evet, bunun korkunç derecede yavaş olduğunun farkındayım ve eğer bazıParent tüm alt sınıflarını fetchAll uygulamaya zorlamazsa çökebilir, ancak soruyu sorulduğu gibi yanıtlar.


2
Hayır, hiç de değil. JaredPar bunu kesinlikle doğru anladı: T.StaticMethodOnSomeBaseClassThatReturnsCollection burada T: SomeBaseClass basitçe SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection belirtmekten farklı değil.
Remi Despres-Smyth

2
İhtiyacım olan şey bu, statik bir yöntemle çalışıyor
myro

İhtiyacım olan cevap buydu çünkü sınıfları ve temel sınıfı kontrol edemiyordum.
Tim

8

Böyle bir yöntemi çağırmanın tek yolu yansıma yoluyla olabilir, ancak bu işlevselliği bir arayüze sarmak ve örnek tabanlı bir IoC / fabrika / vb. Model kullanmak mümkün olabilir.


5

Görünüşe göre C # 'da "sanal statik yöntemler" olmadığı gerçeğini çözmek için jenerikleri kullanmaya çalışıyorsunuz.

Maalesef bu işe yaramayacak.


1
Değilim - oluşturulmuş bir DAL katmanının üzerinde çalışıyorum. Oluşturulan sınıfların tümü, statik FetchAll yöntemine sahip bir temel sınıftan miras alır. Depo sınıfımdaki kod yinelemesini "genel" bir depo sınıfıyla azaltmaya çalışıyorum - kullanılan somut sınıf dışında çok sayıda yinelenen kod.
Remi Despres-Smyth

1
O zaman neden SomeBaseClass.StaticMethod ... () 'u çağırmıyorsun?
Brad Wilson

Üzgünüm, bir önceki yorumda kendimi iyi açıklamadım. FetchAll temelde tanımlanır, ancak türetilmiş sınıflarda uygulanır. Onu türetilmiş sınıfta aramam gerekiyor.
Remi Despres-Smyth

7
Statik bir yöntem ise, o zaman hem temel tarafından tanımlanır hem de uygulanır. C # 'da sanal / soyut statik yöntem diye bir şey yoktur ve bunun için geçersiz kılma yoktur. Sanırım onu ​​yeniden ilan ettiniz, ki bu çok farklı.
Marc Gravell

1
Evet, haklısınız - burada geçersiz varsayımlar yapmıştım. Tartışma için teşekkürler, doğru yola girmeme yardımcı oldu.
Remi Despres-Smyth

2

Şu an için yapamazsınız. Derleyiciye T'nin bu yönteme sahip olduğunu söylemenin bir yoluna ihtiyacınız var ve şu anda bunu yapmanın bir yolu yok. (Birçoğu Microsoft'u genel bir kısıtlamada belirtilebilecekleri genişletmeye zorluyor, bu nedenle bu gelecekte mümkün olabilir).


1
Sorun şu ki, jenerikler çalışma zamanı tarafından sağlandığı için, bu muhtemelen 2.0'dan beri (büyük ölçüde) kaçınılmış olan yeni bir CLR sürümü anlamına geliyor. Belki de yenisini almamız gerekiyordur ...
Marc Gravell

2

Burada işe yarayan bir örnek gönderiyorum, bu bir geçici çözüm

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
Bu benim için bir sözdizimi hatası veriyor mu? Ne anlama public T : SomeBaseClassgeliyor?
Eric

Sınıfınızda someInstanceMethod () örnek yöntemi varsa, bunu her zaman (new T ()) yaparak çağırabilirsiniz. SomeInstanceMethod (); - ancak bu bir örnek yöntemi çağırmaktır - soru, sınıfın statik bir yönteminin nasıl çağrılacağını sordu.
Timothy

2

Ben sadece içeriğe bağlı olarak bazen delegelerin bu sorunları çözdüğünü ortaya koymak istedim.

Statik yöntemi bir tür fabrika veya başlatma yöntemi olarak çağırmanız gerekiyorsa, bir temsilci bildirebilir ve statik yöntemi ilgili genel fabrikaya veya bu "bu statik yöntemle genel sınıf" a ihtiyaç duyan her neyse, iletebilirsiniz.

Örneğin:

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

Ne yazık ki, sınıfın doğru yönteme sahip olmasını zorlayamazsınız, ancak sonuçta ortaya çıkan fabrika yönteminin beklediği her şeye sahip olduğunu (yani, tam olarak doğru imzaya sahip bir başlatma yöntemi) en azından derleme-zamanı zorlayabilirsiniz. Bu, çalışma zamanı yansıtma istisnasından daha iyidir.

Bu yaklaşımın bazı faydaları da vardır, yani init yöntemlerini yeniden kullanabilir, örnek yöntemleri olmasını sağlayabilirsiniz, vb.


1

Bunu burada açıklandığı gibi yansıma kullanarak yapabilmelisiniz

Bağlantının kopması nedeniyle, geri dönüş makinesinde ilgili ayrıntıları buldum:

Statik bir genel yönteme sahip bir sınıfınız olduğunu varsayalım:

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

İlişkiyi kullanarak bu yöntemi nasıl çağırabilirsiniz?

Görünüşe göre çok kolay… Düşünmeyi kullanarak Statik Genel Yöntemi şu şekilde Çağırırsınız:

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

Bağlantı kesildi.
Necronomicron
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.