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

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

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.

Спасибо

Ответы [ 3 ]

22 голосов
/ 02 сентября 2010

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

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

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

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

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

0 голосов
/ 16 сентября 2010

Даже если это было не идеально, я отказался от попыток вернуть что-либо вообще, что позволяет мне сделать что-то подобное:

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

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

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

0 голосов
/ 02 сентября 2010

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

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

Ниже приведен пример того, как это сделать. Я создал параметр 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);

РЕДАКТИРОВАТЬ: изменил мой пример, чтобы использовать другой метод расширения вместо получения пользовательского 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. Это хорошая идея? Я не уверен, но он позволяет компилятору выводить все обобщенные типы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...