web-dev-qa-db-ja.com

ジェネリックパラメーターとのインターフェイスvsジェネリックメソッドとのインターフェイス

私はそのようなインターフェースと具体的な実装を持っているとしましょう

public interface IMyInterface<T>
{
    T My();
}

public class MyConcrete : IMyInterface<string>
{
    public string My()
    {
        return string.Empty;
    }
}

したがって、stringsのMyConcrete実装を作成し、intのもう1つの具体的な実装を作成できます。そしてそれは大丈夫です。しかし、私は同じことをしたいが、ジェネリックメソッドを使用したいので、

public interface IMyInterface2
{
    T My<T>();
}

public class MyConcrete2 : IMyInterface2
{
    public string My<string>()
    {
        throw new NotImplementedException();
    }
}

したがって、同じIMyInterface2がありますが、これはT My<T>()を使用して一般的な動作を定義します。私の具象クラスでは、My動作を実装したいのですが、具象データ型の場合はstringです。しかし、C#ではそれができません。

私の質問は、なぜそれができないのですか?言い換えると、MyInterface<T>の具体的な実装をMyClass : MyInterface<string>として作成し、この時点で汎用性を停止できる場合、なぜ汎用メソッドT My<T>()でそれができないのですか?

34

ジェネリックメソッドの実装もジェネリックである必要があるため、次のようにする必要があります。

_public class MyConcrete2 : IMyInterface2
{
    public T My<T>()
    {
        throw new NotImplementedException();
    }
}
_

ここでMy<string>()できないのはなぜですか?インターフェイスコントラクトにはメソッドが必要なので、任意の型パラメーターTで呼び出すことができ、そのコントラクトを実行する必要があります。

なぜこの点で汎用性を停止できないのか次のような状況が発生するため:

クラス宣言:

_public interface IMyInterface2
{
    T My<T>(t value);
}

public class MyClass21 : IMyInterface2
{
    public string My<string>(string value) { return value; }
}

public class MyClass22 : IMyInterface2
{
    public int My<int>(int value) { return value; }
}
_

使用法:

_var item1 = new MyClass21();
var item2 = new MyClass22();

// they both implement IMyInterface2, so we can put them into list
var list = new List<IMyInterface2>();
list.Add(item1);
list.Add(item2);

// iterate the list and call My method
foreach(IMyInterface2 item in list)
{
    // item is IMyInterface2, so we have My<T>() method. Choose T to be int and call with value 2:
    item.My<int>(2);

    // how would it work with item1, which has My<string> implemented?
}
_
35
MarcinJuraszek

インターフェイスはジェネリックメソッドT My<T>()を宣言しますが、実装はその特定のシグネチャを持つ関数を実装しません。

目的を達成するには、最初の例で、代わりにTジェネリックパラメーターをインターフェイスに提供する必要があります。

public interface IMyInterface2<T>
{
        T My();
}

public class MyConcrete2 : IMyInterface2<string>
{
    public string My()
    {
        throw new NotImplementedException();
    }
}

ジェネリックメソッドを記述するとき、定義はプレースホルダーを保持するためのものです。メソッドを呼び出すと、実際のタイプが表示されます。代わりに、あなたは書くべきです

public T My<T>()
{
    throw new NotImplementedException();
}

メソッドを呼び出すときは、そこで文字列を使用できます。

3
Rakesh

ソリューションは2つの理由で機能しません。

まず、インターフェイスは契約です。 IMyInterface2を実装するとき、ジェネリック型パラメーターを受け取り、その型を返すMyという名前の関数を実装することを保証します。 MyConcrete2はこれを行いません。

第二に、C#ジェネリックでは、型パラメーターの特殊化は一切許可されません。 (C#がこれをサポートすることを望みます。)これは、サンプルがコンパイルされるC++テンプレートでは一般的なことですが、Myを呼び出していない場合、MyConcrete2の使用はコンパイルに失敗します。 string

1
IngisKahn