5

次の完全な状況で C# が型を推論しない理由がわかりません。

public interface IThing {}

public class Thing1 : IThing {}

public class Thing2 : IThing {}

public interface IContainer {}

public class Container1 : IContainer
{
    public IThing A { get { return new Thing1(); } }
    public IThing B { get { return new Thing2(); } }
}

public class Container2 : IContainer
{
    public IThing C { get { return new Thing1(); } }
    public IThing D { get { return new Thing2(); } }
}

public class SomeClass
{
    public void PerformTask() {}
}

public static class ExtensionMethods
{
    // This function behaves as I would expect, inferring TContainer
    public static TContainer DoStuffWithThings<TContainer>(this TContainer container, Func<TContainer, IThing> getSomething, Func<TContainer, IThing> getSomethingElse) 
        where TContainer : IContainer
    {
        var something = getSomething.Invoke(container);
        var somethingElse = getSomethingElse.Invoke(container);

        // something and something else are the things we specify in our lambda expressions with respect to the container

        return container;
    }

    // The method in question
    public static TCustomReturnType DoStuffWithThings<TContainer, TCustomReturnType>(this TContainer container, Func<TContainer, IThing> getSomething, Func<TContainer, IThing> getSomethingElse)
        where TContainer : IContainer
        where TCustomReturnType : new()
    {
        var something = getSomething.Invoke(container);
        var somethingElse = getSomethingElse.Invoke(container);

        // Do stuff with the things just as above

        // This time we return our custom type
        return new TCustomReturnType();
    }
}

public class Driver
{
    public static void Main(string[] args)
    {
        var container1 = new Container1();
        var container2 = new Container2();
        // I can do stuff with the things for each container, returning the container each time.

        container1.DoStuffWithThings(c => c.A, c => c.B)
                  .DoStuffWithThings(c => c.B, c => c.A);

        container2.DoStuffWithThings(c => c.C, c => c.D)
                  .DoStuffWithThings(c => c.D, c => c.C);

        // Now we try to do the same but with the custom return type
        container1.DoStuffWithThings<Container1, SomeClass>(c => c.A, c => c.B)
            .PerformTask();
        // As you can see, the compiler requires us to specify Container1 as the container type.
        // Why is it not inferred? It is called from an instance of Container1.
        // The behavior I expect is for container1.DoStuffWithThings<SomeClass>(...) to infer
        // the container type and return a new instance of SomeClass.
    }
}

基本的な考え方は、ジェネリック型パラメーターが 1 つしかない場合、コンパイラーはその型を推測するというものです。2 番目を追加すると、コンパイラも推論しません (明らかに 2 番目を推論できませんが、1 番目を推論できない理由はわかりません)。私の質問は、コンテナのタイプが推測されないのはなぜですか?

4

1 に答える 1

9

その理由は、2番目のタイプのパラメーターがどの関数パラメーターでも使用されていないためです。したがって、そのタイプをパラメータの使用法から純粋に推測する方法はありません。

このようなメソッドシグネチャがある場合(明らかにコードと同等ではなく、単なる例です):

public static TResult DoStuffWithThings<TContainer, TResult>(
    this TContainer container,
    Func<TContainer, TResult> getSomething)

次に、パラメーターからジェネリック型を推測できるようになります

最初のパラメーターの指定を避けたい場合は、メソッドを2つの関数に分割し、次のようにジェネリック型の中間パラメーターを非表示にすることができます。

public static IntermediateResult<T> DoStuffWithThings<T>(this T container)

public class IntermediateResult<T>
{
    public WithReturnType<TResult>()
}

それからそれは

var result = container.DoStuffWithThings().WithReturnType<Result>();
于 2013-03-16T02:38:49.533 に答える