web-dev-qa-db-ja.com

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

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


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

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

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

97

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

T.StaticMethodOnSomeBaseClassThatReturnsCollection

...以下と同じです:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

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

53
JaredPar

前回の回答を詳しく説明すると、ここで望むものに反射が近いと思います。私はあなたが何かをすべきかすべきでない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の実装を強制しない場合にクラッシュする可能性があることを知っていますが、質問に答えます。

25
Joshua Pech

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

7
Marc Gravell

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

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

5
Brad Wilson

ここで、私は動作する例を投稿します、それは回避策です

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
rodrijp

今のところ、できません。 Tがそのメソッドを持っていることをコンパイラに伝える方法が必要であり、現在、それを行う方法はありません。 (多くの場合、Microsoftにジェネリック制約で指定できるものを拡張するように促しているため、将来的にはこれが可能になるかもしれません)。

2
James Curran

状況によっては、デリゲートがこれらの問題を解決することもあるので、それを捨てたいだけです。

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

例えば:

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メソッドを再利用したり、インスタンスメソッドにしたりできます。

2
Amir Abiri

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

1
johnc