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

时间:2018-12-14 12:16:47

标签: c# asp.net generics html-helper asp.net-mvc-5.2

我创建了一个扩展方法,该方法应该为包含IList<T>类型的项目的表提供表标题文本。虽然,编译器说无法从用法中推断出TModel,但是对我来说,很明显,如果类型参数约束指出TListModelIList<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>

1 个答案:

答案 0 :(得分:2)

这是先前向C#语言团队提出的请求:REQUEST: Pattern matching/better type inferencing with generics. #5023。但是,请注意链接的注释,这会引入重大更改,因此当前未实现。另请参见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)

进一步阅读:How can I return <TEnumerable, T> : where TEnumerable:IEnumerable<T>,请参阅乔恩·斯基特(Jon Skeet)的答案。