Делегаты с выводом типа возвращаемого значения (C #) - PullRequest
4 голосов
/ 12 мая 2009

Я все еще новичок в работе с делегатами, и я играю со слоем доступа к данным на основе делегатов, описанным в книге Стивена Джона Метскера «Шаблоны проектирования в C #» (отличное чтение!). Он определяет делегата доступа к данным следующим образом:

public delegate object BorrowReader(IDataReader reader);

Результатом использования этого кода является код, который выглядит следующим образом:

var result = Foo.Bar(new BorrowReader(DoFooBarMagic));
var result = Foo.Bar(DoFooBarMagic);

Тем не менее, поскольку тип возвращаемого делегата - «объект», вам необходимо выполнить приведение, чтобы получить то, что метод (в данном примере «DoFooBarMagic») действительно возвращает. Поэтому, если «DoFooBarMagic» возвращает List, вам нужно сделать что-то вроде этого:

var result = Foo.Bar(DoFooBarMagic) as List<string>;

Что я хотел бы, так это иметь возможность пропустить приведение и получить тип возврата делегата, выведенный из типа возврата метода делегата. Я подумал, что, возможно, есть способ использовать параметр Type для вывода типа возвращаемого значения. Примерно так:

public delegate T BorrowReader<T>(IDataReader reader);
List<string> result = Foo.Bar(new BorrowReader(DoFooBarMagic)); 
//Look, Ma, no cast!
var result2 = Foo.Bar(DoFooBarMagic);

Где тип возврата выводится из типа возврата метода делегата, но, похоже, это не работает. Вместо этого вы должны сделать это:

public delegate T BorrowReader<T>(IDataReader reader);
var result = Foo.Bar(new BorrowReader<List<string>>(DoFooBarMagic));

Что вряд ли кажется лучше, чем у актеров.

Так есть ли способ вывести тип возврата делегата из типа возврата метода делегата?

Изменить для добавления: Я могу изменить подпись Foo.Bar, если это будет необходимо. Текущая подпись по сути такова:

public static T Bar<T>(string sprocName,
                       DbParameter[] params, 
                       BorrowReader<T> borrower);

Примечание: эта подпись является результатом текущего состояния, которое использует это определение делегата:

public delegate T BorrowReader<T>(IDataReader reader);

Ответы [ 6 ]

6 голосов
/ 12 мая 2009

Как насчет:

public static T Bar2<T>(Func<IDataReader,T> func) where T : class
{
    BorrowReader borrower = new BorrowReader(func);
    return (T) Foo.Bar(borrower);
}

Я знаю, что он все равно выполняет каст, что ужасно, но это должно сработать. (Первоначально я думал, что вы можете избежать неявного преобразования из func, но, очевидно, нет. По крайней мере, до C # 4.0.)

Конечно, если вы можете изменить подпись Foo.Bar на общую, вы смеетесь ...

РЕДАКТИРОВАТЬ: Чтобы ответить на комментарий: если сигнатура метода изменяется, чтобы принять универсальный делегат, например,

public static T Bar<T>(Func<IDataReader, T> func)

тогда вызывающий код может почти просто быть:

var result = Foo.Bar(DoFooBarMagic);

К сожалению, вывод типов не работает с группами методов, поэтому вы должны использовать либо:

Func<IDataReader, List<string>> func = DoFooBarMagic;
var result = Foo.Bar(func);

или (лучше, если немного менее эффективно)

var result = Foo.Bar(reader => DoFooBarMagic(reader));

Итак, вы правы - этот ответ не дал ОП точно того, что требовалось, но, по-видимому, он подошел достаточно близко, чтобы получить признание. Надеюсь, это редактирование поможет объяснить остальное:)

0 голосов
/ 26 января 2011

Я ожидаю, что F # может сделать это. Я не эксперт, но F # использует вывод типа возврата ( подробности о MSDN ).

0 голосов
/ 13 мая 2009
// if BorrowReader is generic...
public delegate T BorrowReader<T>(IDataReader reader);

public class Foo
{
    // ... and Foo.Bar() is also generic
    public static T Bar<T>(BorrowReader<T> borrower) { ... }

    public void SomeMethod()
    {
        // this does *not* work (compiler needs more help)
        var result1 = Foo.Bar(DoFooBarMagic);

        // but instead of this (which works)
        var result2 = Foo.Bar(new BorrowReader<List<string>>(DoFooBarMagic));

        // you can do this (also works)
        // which emits the same IL, anyway
        var result3 = Foo.Bar<List<string>>(DoFooBarMagic);
    }
}
0 голосов
/ 12 мая 2009

Я не думаю, что есть способ обойти это. По крайней мере, не так, как вы предлагаете это. Основная проблема заключается в том, что, поскольку он должен быть выведен во время компиляции, если у вас нет явного возвращаемого типа, компилятор не может на самом деле вывести возвращаемый тип.

0 голосов
/ 12 мая 2009

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

0 голосов
/ 12 мая 2009

Это ужасно, но вы можете использовать параметр out. Из моего парсера enum:

public static T Parse<T>(string value)
{
    // return a value of type T
}

public static void Parse<T>(string value, out T eValue)
{
    // do something and set the out parameter
}

// now can be called via either
SomeEnum blah = Enums.Parse<SomeEnum>("blah");

// OR
SomeEnum blah;
Enums.Parse("blah", out blah);

Второй выводит тип, но, как я уже сказал, это ужасно.

...