Преобразование группы методов расширения в делегат с универсальным типом - PullRequest
13 голосов
/ 07 марта 2012

У меня есть два метода расширения в IDataReader со следующими сигнатурами:

internal static List<T> GetList<T>(this IDataReader reader, Func<string, T> del)

internal static double? GetDoubleOrNull(this IDataReader reader, string columnName)

GetDoubleOrNull не перегружен.

В другом месте я могу сделать

Func<string, double?> del = reader.GetDoubleOrNull;

var x = reader.GetList(del);

или

var x = reader.GetList<double?>(reader.GetDoubleOrNull);

или просто передайте метод экземпляра, такой как

public double? blah(string s)

var x = reader.GetList(blah);

но я не могу сделать

var x = reader.GetList(reader.GetDoubleOrNull);

Компилятор выдает ошибку

cannot convert from 'method group' to 'System.Func<string,double?>'

Я не понимаю этого. Я подумал, что, поскольку на GetDoubleOrNull нет перегрузки, разрешение на перегрузку не будет, и он может вывести параметр типа из сигнатуры метода.

Действительно запутанная часть - как это работает при передаче blah.

Ответы [ 2 ]

8 голосов
/ 07 марта 2012

Предисловие: перейдите к редактированию, если хотите получить полное объяснение.Спойлер: Методы расширения - это хитрость компилятора, и на самом деле они имеют еще один аргумент, чем они выглядят, когда вы вызываете их .

Это в разрешении группы методов.По-видимому, компилятор C # не тратит время на выяснение того, имеет ли используемый вами метод перегрузки или нет;это просто всегда требует явного приведения.Извлечь:

Что такое группа методов в C #?
Вывод метода не работает с группой методов

Группа методов, котораявозвращается из reader.GetDoubleOrNull сужается из-за того, что вы пытаетесь привести его к: GetDoubleOrNull может ссылаться на любое количество перегруженных методов с этим именем.Вы должны явно привести его.

Интересно, что вы даже не можете назначить группу методов неявно типизированной переменной по той же причине:

var x = reader.GetDoubleOrNull;

не компилируется, потому что для этого требуетсяявное приведение.

Edit Я почти уверен, что путаница здесь связана с методами расширения:

Проверьте следующий тестовый класс:

public static class Extensions
{
    public static List<T> GetList<T>(this IDataReader reader, Func<string, T> del)
    {
        throw new NotImplementedException();
    }

    public static double? GetDoubleOrNull(this IDataReader reader, string columnName)
    {
        throw new NotImplementedException();
    }

    public static double? blah(this string s)
    {
        throw new NotImplementedException();
    }
}

Вы можете успешно позвонить

var x = reader.GetList(Extensions.blah);

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

public static List<T> GetList2<T>(this IDataReader reader, Func<IDataReader, string, T> del) 
{ 
    throw new NotImplementedException(); 
}

Теперь вы можете вызвать

x = reader.GetList2(Extensions.GetDoubleOrNull);

, и он будет правильно скомпилирован.Что дает?

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

В вашем коде вы вызываете метод расширения с синтаксисом метода экземпляра.Однако промежуточный язык (IL), сгенерированный компилятором, переводит ваш код в вызов статического метода.Следовательно, принцип инкапсуляции действительно не нарушается.Фактически, методы расширения не могут получить доступ к закрытым переменным в типе, который они расширяют.

Итак, когда вы вызываете

var x = reader.GetDoubleOrNull("myColumnName");

, то, что на самом деле компилируется и выполняется, по сути это (вполне законный вызов, хотя это и метод расширения):

var x = Extensions.GetDoubleOrNull(reader, "myColumnName");

Итак, когда вы пытаетесь использовать GetDoubleOrNull в качестве аргумента для Func<string, double?>, компилятор собирается "Я могу включить GetDoubleOrNull в Func<IDataReader, string, double?>, потому что у него есть два аргумента, но я не знаю, как превратить его в Func<string, double?> "

Даже если вы называете его как экземплярметод IDataReader с одним аргументом, это не : это просто статический метод с двумя аргументами, который C # заставил вас думать, что он является частью IDataReader.

0 голосов
/ 07 марта 2012

GetDoubleOrNull возвращает двойной? GetList ожидает IDataReader и System.Func<string,int?>

Сообщение об ошибке немного вводит в заблуждение

публичный дубль? бла (строка s)

var x = reader.GetList (бла);

бла является делегатом в этом призыве. В GetList (GetDoubleOrNull) это результат get doubleOrNull.

Хороший вопрос. Отправьте ответ, помогло мне или нет.

...