ジェネリック型パラメーターで静的メソッドを呼び出す


107

私はこのようなことをしたいと思っていましたが、C#では違法であるようです:


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

「T」は「タイプパラメータ」であり、指定されたコンテキストでは無効です。コンパイル時エラーが発生します。

ジェネリック型パラメーターが指定された場合、ジェネリッククラスで静的メソッドを呼び出すにはどうすればよいですか?制約が与えられた場合、静的メソッドが利用可能である必要があります。


回答:


58

この場合、制約された型の静的メソッドを直接呼び出すだけです。C#(およびCLR)は、仮想静的メソッドをサポートしていません。そう:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

...以下と同じにすることができます:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

ジェネリック型パラメーターを通過することは、不要な間接参照であるため、サポートされていません。


25
しかし、静的メソッドを子クラスでマスクするとどうなるでしょうか。public class SomeChildClass:SomeBaseClass {public new static StaticMethodOnSomeBaseClassThatReturnsCollection(){}}ジェネリック型からその静的メソッドにアクセスするために何かできますか?
Hugo Migneron、2010

2
以下のJoshua Pechの回答を確認してください。その場合はうまくいくと思います。
Remi Despres-Smyth

1
うまくいくでしょうreturn SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();か?もしそうなら、あなたはそれをあなたの答えに加えたいかもしれません。ありがとう。それは私のために働いた。私の場合、私のクラスにはタイプparamがあったのでreturn SomeBaseClass<T>.StaticMethodOnSomeBaseClassThatReturnsCollection();、それを実行しました。
toddmo 2016年

27

以前の回答について詳しく述べると、ここでの反省はあなたが望むものに近いと思います。あなたが何かをすべきか、すべきでない理由を1001挙げることができます。私は尋ねられた質問に答えます。ジェネリックパラメーターの型でGetMethodメソッドを呼び出して、そこから移動する必要があると思います。たとえば、関数の場合:

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()を持つクラスです。

はい、私はこれが恐ろしく遅いことを知っており、someParentがすべての子クラスにfetchAllの実装を強制しない場合にクラッシュする可能性がありますが、尋ねられた質問に答えます。


2
いいえ、まったくありません。JaredParはそれを完全に正しく理解しました。
Remi Despres-Smyth、2011

2
これは私が必要としたもので、静的メソッドで動作します
myro

クラスと基本クラスを制御できなかったため、これが必要な答えでした。
ティム

8

このようなメソッドを呼び出す唯一の方法はリフレクションを経由することですが、その機能をインターフェイスにラップし、インスタンスベースのIoC /ファクトリー/などのパターンを使用することは可能なように思えます。


5

ジェネリックを使用してC#に「仮想静的メソッド」がないという事実を回避しようとしているようです。

残念ながら、それはうまくいきません。


1
私は違います-生成されたDALレイヤーの上で作業しています。生成されたクラスはすべて、静的FetchAllメソッドを持つ基本クラスから継承します。私のリポジトリクラスのコードの重複を、「汎用」リポジトリクラス-使用する具象クラスを除いて多くの繰り返しコード-で削減しようとしています。
Remi Despres-Smyth、

1
それでは、なぜSomeBaseClass.StaticMethod ...()を呼び出さないのですか?
Brad Wilson、

申し訳ありませんが、私は前のコメントでよく説明していませんでした。FetchAllはベースで定義されていますが、派生クラスで実装されています。派生クラスで呼び出す必要があります。
Remi Despres-Smyth

7
静的メソッドの場合は、ベースによって定義および実装されます。C#には仮想/抽象静的メソッドなどはなく、そのようなオーバーライドもありません。あなたは単にそれを再宣言したのではないかと思いますが、それは非常に異なります。
Marc Gravell

1
はい、あなたは正しいです-私はここで無効な仮定をしました。議論をありがとう、それは私を正しい軌道に乗せるのに役立ちました。
Remi

2

今のところできません。Tがそのメソッドを持っていることをコンパイラーに伝える方法が必要ですが、現在、その方法はありません。(多くは、マイクロソフトに一般的な制約で指定できるものを拡張するように強いているので、おそらくこれは将来的に可能になるでしょう)。


1
問題は、ジェネリックスがランタイムによって提供されるため、これはおそらく新しいCLRバージョンを意味することになります-2.0以降では(大部分)回避されています。多分私達は新しいもののためにあるかもしれません...
マーク・グラベル

2

ここで、私はうまくいく例を投稿します、それは回避策です

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
これは私に構文エラーを与えますか?どういうpublic T : SomeBaseClass意味ですか?
Eric

クラスにインスタンスメソッドsomeInstanceMethod()がある場合は、常に(new T())。someInstanceMethod(); -しかし、これはインスタンスメソッドを呼び出しています-クラスの静的メソッドを呼び出す方法を質問しました。
timothy

2

状況によっては、デリゲートがこれらの問題を解決することもあります。

静的メソッドを何らかのファクトリまたは初期化メソッドとして呼び出す必要がある場合は、デリゲートを宣言して、静的メソッドを関連する汎用ファクトリまたはこの「静的メソッドを持つ汎用クラス」を必要とするものに渡すことができます。

例えば:

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

残念ながら、クラスに適切なメソッドがあることを強制することはできませんが、少なくともコンパイル時に、結果のファクトリーメソッドが期待するすべてのもの(つまり、正確に正しいシグネチャを持つ初期化メソッド)を持っていることを強制できます。これは、実行時リフレクション例外よりも優れています。

このアプローチにはいくつかの利点もあります。つまり、initメソッドを再利用したり、インスタンスメソッドにしたりすることができます。


1

ここで説明するように、リフレクションを使用してこれを行うことができるはずです

リンクが停止しているため、ウェイバックマシンに関連する詳細が見つかりました。

静的なジェネリックメソッドを持つクラスがあるとします。

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

リレーションを使用してこのメ​​ソッドをどのように呼び出すことができますか?

非常に簡単であることがわかります…これが、リフレクションを使用して静的ジェネリックメソッドを呼び出す方法です。

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

リンクが停止しています。
Necronomicron
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.