Подтвердить что ты не робот

Общий метод расширения: аргумент типа не может быть выведен из использования

Я пытаюсь создать универсальный метод расширения, который работает на типизированных таблицах данных:

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, - это то, что "Expression < Func < T, bool > >" будет набираться и просматриваться InteliSence.

Спасибо

4b9b3361

Ответ 1

Вывод типа метода не делает вывод из аргументов в ограничения. Он делает выводы из аргументов формальным параметрам, а затем проверяет, удовлетворяют ли заключения, сделанные из аргументов формалам, ограничениям.

В вашем случае недостаточно данных из аргументов, чтобы вывести, что параметры типа, не рассмотрев сначала ограничения, которые мы не собираемся делать, пока не проверим выводы по ограничениям. Простите об этом, но о том, как указывается алгоритм определения типа.

Меня спрашивали об этом много раз, и, как представляется, консенсус состоит в том, что я морально ошибаюсь в том, что я поддерживаю позицию, которую вывод должен вывести из аргументов только в формальные параметры. Примерно дюжину людей говорят мне, что я ошибаюсь в этом отношении, см. Комментарии к моему анализу этой тесно связанной проблемы:

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

Я сохраняю свою позицию.

Ответ 2

Ответ Эрика отлично подходит для объяснения того, почему типы не могут быть выведены. Вот несколько предложений, которые, надеюсь, сократят многословие кода, которое вам нужно будет написать.

Если вы можете явно определить тип вашего лямбда-выражения, тогда он может вывести типы.

Ниже приведен пример того, как это сделать. Я создал параметр criteria, который явно имеет тип Expression<Func<MyTypedDataSet.MyTypedRow, bool>>. В этом примере это не поможет вам много набирать текст, но, на практике, вы можете использовать это.

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

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

        return table.DoSomething(criteria);

EDIT: изменил мой пример, чтобы использовать другой метод расширения, а не выводить пользовательский класс TypedTableBase<T> из System.Data.TypedTableBase<T>.

Ниже приведен еще один пример, который может лучше выполнить вывод параметров типа. Вы определяете другой метод расширения (мой называется RowPredicate), который имеет только один параметр типа для вывода. Первый параметр имеет тип TypedTableBase<RowType>, поэтому компилятор не должен иметь проблем с выводом типа из этого:

    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"));

и компилятор правильно отображает тип...

Спасибо вам за ваши ответы.