如果类型参数约束指定了类型,为什么不能从用法中推断出类型参数?

Why type argument cannot be inferred from usage if type argument constraint specifies the type?

我创建了一个扩展方法,它应该为 table 提供 table header 文本,其中包含 collection 类型的项目 IList<T>.虽然,编译器说 TModel 不能从用法中推断出来,但是,对我来说很明显,如果类型参数约束说 TListModel 是一个 IList<TModel>,那么确实可以推断出 TModel .

public static MvcHtmlString HeaderFor<TListModel, TModel, TValue>(this HtmlHelper<TListModel> listViewModel, Expression<Func<TModel, TValue>> expression) 
    where TListModel : IList<TModel> 

TListModelList<Product>,所以 TModelProduct,因此,我想像这样使用 HtmlHelper:

<th scope="col" class="azonosito">
     @Html.HeaderFor(x => x.Price)
</th>

现在只好这样用了,好别扭:

<th scope="col" class="azonosito">
     @Html.HeaderFor((Product x) => x.Price)
</th>

这是之前向 C# 语言团队提出的请求:REQUEST: Pattern matching/better type inferencing with generics. #5023. However, note the linked comment, this introduces breaking changes, so is not currently implemented. See also https://github.com/dotnet/roslyn/issues/11242

问题是编译器无法推断 TListModel 的类型只能是 IList<TModel>。它不理解列表成员 (IList<TModel>) 和容器 (TListModel) 之间存在联系。

有几种方法可以处理这个问题。正如您所发现的,一种方法是显式地向 lambda 提供类型,尽管您也可以显式地提供类型参数:@{Html.HeaderFor<IList<SomeModel>, SomeModel, int>(x => x.Price)}

对于实际的解决方案,您可以更改扩展方法定义。此更改应将成员类型作为参数的一部分提供给扩展方法。

public static MvcHtmlString HeaderFor<TModel, TValue>(this HtmlHelper<IList<TModel>> listViewModel, Expression<Func<TModel, TValue>> expression)
{
    return new MvcHtmlString("a");
}  

这允许在没有编译器错误的情况下进行隐式类型推断:

@model IList<SomeModel>
...
@Html.HeaderFor(x => x.Price)

进一步阅读:,参见 Jon Skeet 的回答。