1. ホーム
  2. c#

[解決済み] なぜC#は私のジェネリック型を推論しないのですか?

2023-05-28 20:06:40

質問

私はジェネリックメソッドでたくさんのFuncy楽しい(楽しみを意図して)持っています。ほとんどの場合、C# の型推論は十分に賢く、私のジェネリック メソッドで使用しなければならないジェネリック引数を見つけることができますが、今私は、C# コンパイラーが正しい型を見つけることに成功したと信じている一方で、成功しないデザインを手に入れました。

この場合、コンパイラーは少し頭が悪いのか、それとも私のジェネリック引数を推論できない非常に明確な理由があるのか、誰か教えていただけませんか。

これがそのコードです。

クラスとインターフェースの定義です。

interface IQuery<TResult> { }

interface IQueryProcessor
{
    TResult Process<TQuery, TResult>(TQuery query)
        where TQuery : IQuery<TResult>;
}

class SomeQuery : IQuery<string>
{
}

コンパイルできないコードもあります。

class Test
{
    void Test(IQueryProcessor p)
    {
        var query = new SomeQuery();

        // Does not compile :-(
        p.Process(query);

        // Must explicitly write all arguments
        p.Process<SomeQuery, string>(query);
    }
}

これはなぜでしょうか?何が足りないのでしょうか?

これがコンパイラのエラーメッセージです(あまり想像の域を出ませんね)。

メソッドIQueryProcessor.Process<TQuery.TResult>の型引数は、使用方法から推測できません。 TResult>(TQuery) の型引数は、使用方法から推測することができません。明示的に を明示的に指定してみてください。

C#が推論できるはずだと思うのは、次のような理由です。

  1. を実装したオブジェクトを提供する。 IQuery<TResult> .
  2. それのみ IQuery<TResult> のバージョンは、その型が実装している IQuery<string> でなければならず、したがって TResult は string .
  3. この情報により、コンパイラはTResultとTQueryを持つことになります。

どのように解決するのですか?

これは元々 質問 に投稿されたもので、OPに代わってここに移動されました。

私にとっての最良の解決策は IQueryProcessor インターフェースを変更し、実装で動的型付けを使用することでした。

public interface IQueryProcessor
{
    TResult Process<TResult>(IQuery<TResult> query);
}

// Implementation
sealed class QueryProcessor : IQueryProcessor {
    private readonly Container container;

    public QueryProcessor(Container container) {
        this.container = container;
    }

    public TResult Process<TResult>(IQuery<TResult> query) {
        var handlerType =
            typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
        dynamic handler = container.GetInstance(handlerType);
        return handler.Handle((dynamic)query);
    }
}

IQueryProcessor インタフェースは、現在 IQuery<TResult> パラメータを受け取るようになりました。こうすることで TResult を返すことができ、これで消費者の立場から見た問題は解決します。具体的なクエリ型が必要なので(私の場合)、実際の実装を得るために実装でリフレクションを使う必要があります。しかし、ここでダイナミックタイピングが登場し、私たちのためにリフレクションを行ってくれる。これについては、以下の記事を参照してください。 記事 .