具有多个泛型类型参数的类型推断

6

我不明白为什么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个回答

10

原因在于第二个类型参数没有在任何函数参数中使用。因此,它的类型无法仅从参数使用中推断出来。

如果您有这样的方法签名(显然不等同于您的代码,只是一个例子):

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

然后它将能够从参数中推断出通用类型。

如果您想避免指定第一个参数,可以将该方法拆分成两个函数,并将中间参数隐藏在通用类型中,就像这样:

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

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

然后它被称为:
var result = container.DoStuffWithThings().WithReturnType<Result>();

我并不希望编译器推断第二个参数(正如你所说,它在任何参数中都没有被使用)。但是我期望它可以推断第一个参数(它在参数中)。这样我就可以调用 container1.DoStuffWithThings<TResult>(...) 并让它推断 TContainer 是 Container1。在进行类型推断时,参数是全部还是无法推断的? - pagrick
4
你必须明确地指定所有类型参数或不指定,但请参考我的更新回答以获取一种替代方案。 - p.s.w.g
1
@perrick 还要考虑到这一点,很难区分 Foo<T>(...) (一个类型参数,明确声明)和 Foo<T, TInferred>(两个类型参数,只有一个明确声明)。 - p.s.w.g
1
如果编程语言允许使用Foo<,T>(...)的语法,那就太好了,这样可以明确第二个类型参数是你要指定的,同时允许推断第一个类型参数。在这种情况下,Foo(...)将成为Foo<,>(...)的快捷方式。 - jam40jeff
"您必须指定所有类型参数或不指定任何类型参数" - 这正是我所需要的。 - stuartd
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接