一尘不染

C#为什么不推断我的泛型类型?

c#

我在泛型方法中有很多有趣的乐趣(希望有乐趣)。在大多数情况下,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)的类型参数。尝试显式指定类型参数。

我认为C#应该能够推断出它的原因是由于以下原因:

  1. 我提供了一个实现的对象IQuery<TResult>
  2. IQuery<TResult>类型实现的唯一版本是IQuery<string>,因此TResult必须是string
  3. 利用此信息,编译器将具有TResult和TQuery。

对我来说,最好的解决方案是更改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>参数。这样,它可以返回a
TResult,这将从消费者的角度解决问题。我们需要在实现中使用反射来获得实际的实现,因为需要具体的查询类型(在我的情况下)。但是这里有动态类型来进行救援,这将为我们做反思。您可以在本文中阅读有关此内容的更多信息。


阅读 391

收藏
2020-05-19

共1个答案

一尘不染

一群人指出,C#不会基于约束进行推理。这是正确的,并且与问题有关。通过检查 参数 及其对应的 形式参数类型 来进行推断,这是推断信息的唯一来源。

然后,很多人都链接到这篇文章:

http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-
in​​ference-does-not-work-on-member-
groups.aspx

那篇文章既过时又与这个问题无关。这是过时的,因为它描述了我们在C#3.0中做出的设计决策,然后在C#4.0中撤销了该决策,主要是基于对该文章的回答。我刚刚在文章中添加了对此效果的更新。

这无关紧要,因为本文涉及 从方法组参数到泛型委托形式参数的返回类型推断 。那不是原始海报问的情况。

我要阅读的相关文章是这样的:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-
part-of-the-
signature.aspx

更新:我听说有消息说C#7.3稍微改变了何时应用约束的规则,使上述已有十年历史的文章不再准确。有空的时候,我将回顾一下以前同事所做的更改,看看是否值得在我的新博客上发布更正;在此之前,请谨慎使用,并查看C#7.3的实际操作。

2020-05-19