通用扩展方法:无法从用法推断类型参数

Posted

技术标签:

【中文标题】通用扩展方法:无法从用法推断类型参数【英文标题】:Generic extension method : Type argument cannot be inferred from the usage 【发布时间】:2011-04-07 12:09:48 【问题描述】:

我正在尝试创建一个适用于类型化数据表的通用扩展方法:

public static class Extensions

    public static TableType DoSomething<TableType, RowType>(this TableType table, param Expression<Func<RowType, bool>>[] predicates)
        where TableType : TypedTableBase<RowType>
        where RowType : DataRow
    
        // do something to each row of the table where the row matches the predicates
        return table;
    

    [STAThread]
    public static void main()
    
        MyTypedDataSet.MyTypedDataTable table = getDefaultTable();
    

    public static MyTypedDataSet.MyTypedDataTable getDefaultTable()
    
        // this line compiles fine and does what I want:
        return new MyTypedDataSet.MyTypedDataTable().DoSomething<MyTypedDataSet.MyTypedDataTable, MyTypedDataSet.MyTypedRow>(row => row.Field1 == "foo");

        // this line doesn't compile :
        return new MyTypedDataSet.MyTypedDataTable().DoSomething(row => row.Field1 == "foo");
        // Error : The type arguments .. cannot be inferred from the usage
    

第一行效果很好,但真的很难看... 第二行无法编译,因为编译器无法推断 RowType 的类型。 这个方法将被许多不同的程序员用作 DataLayer 的一部分,所以我宁愿不需要他们指定 TypeParameter。 编译器不应该知道 RowType 与 TypedTableBase 使用的类型相同吗?

由于在此代码示例中可能不明显的不同原因,我确实需要以原始形式返回数据表。我需要 RowType 的原因是 InteliSence 将输入并看到“Expression&lt;Func&lt;T, bool>&gt;”。

谢谢

【问题讨论】:

【参考方案1】:

方法类型推断不会根据参数对约束进行推断。它从参数到形式参数进行推断,然后检查从参数到形式的推断是否满足约束。

在您的情况下,没有足够的参数数据来推断类型参数是什么而不首先查看约束,我们不会这样做直到我们检查对约束的推论。对此很抱歉,但这就是指定类型推断算法的方式。

我已经多次被问到这个问题,并且一致认为我在道德上错误地坚持推断应该仅从参数推断到形式参数的立场。大约有十几个人告诉我我在这方面的想法是错误的,请参阅我对这个密切相关问题的分析的 cmets:

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

我保持我的立场。

【讨论】:

这是过期链接的 WayBack Machine 版本:web.archive.org/web/20100922123630/http://blogs.msdn.com/b/…【参考方案2】:

Eric 的回答非常适合解释为什么无法推断类型。这里有一些建议,希望可以减少您必须编写的代码的冗长性。

如果您可以显式定义 lambda 表达式的类型,那么它可以推断类型。

如何做到这一点的一个例子如下。我创建了一个criteria 参数,它显式地属于Expression&lt;Func&lt;MyTypedDataSet.MyTypedRow, bool&gt;&gt; 类型。在这个例子中,这并没有为你节省太多的打字时间,但也许在实践中你可以利用它。

        MyTypedDataSet.MyTypedDataTable table = new MyTypedDataSet.MyTypedDataTable();

        Expression<Func<MyTypedDataSet.MyTypedRow, bool>> criteria = row => row.Field1 == "foo";

        return table.DoSomething(criteria);

编辑:更改我的示例以使用另一种扩展方法,而不是从 System.Data.TypedTableBase&lt;T&gt; 派生自定义 TypedTableBase&lt;T&gt; 类。

下面是另一个可以更好地推断类型参数的示例。您定义了另一种扩展方法(我的称为RowPredicate),它只有一个类型参数要推断。第一个参数的类型是TypedTableBase&lt;RowType&gt;,所以编译器应该可以毫无问题地从中推断出类型:

    public static Expression<Func<RowType, bool>> RowPredicate<RowType>(this TypedTableBase<RowType> table, Expression<Func<RowType, bool>> predicate)
        where RowType : DataRow
    
        return predicate;
    

这允许您编译以下代码:

        MyTypedDataSet.MyTypedDataTable table = new MyTypedDataSet.MyTypedDataTable();

        return table.DoSomething(table.RowPredicate(row => row.Field1 == "foo"));

table 参数主要用于通知编译器RowType 使用的类型。这是一个好主意吗?我不太确定,但它确实允许编译器推断所有泛型类型。

【讨论】:

【参考方案3】:

即使这并不理想,我也放弃了尝试返回任何允许我执行以下操作的东西:

public static void DoSomething<RowType>(this TypedTableBase<RowType> table, param Expression<Func<RowType, bool>>[] predicates)
    where RowType : DataRow
    
        // do something to each row of the table where the row matches the predicates
        // do not return the table... too bad for chaining commands
    

然后像这样使用它:

MyTypedDataSet.MyTypedDataTable table = new MyTypedDataSet.MyTypedDataTable();
table.DoSomething(row => row.Field1 == "foo"));

并且编译器会正确推断类型。

感谢两位的回答。

【讨论】:

以上是关于通用扩展方法:无法从用法推断类型参数的主要内容,如果未能解决你的问题,请参考以下文章

无法从用法中推断出类型参数。尝试显式指定类型参数

C# 泛型方法类型参数不是从用法中推断出来的

将类型传递给通用 Swift 扩展,或者理想情况下推断它

Rust 无法推断通用特征 impl 的返回类型

TypeScript 无法在扩展语法函数调用中推断数组类型

为啥 C# 无法从非泛型静态方法的签名推断泛型类型参数类型?