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

Преобразование группы методов расширения в делегат с общим типом

У меня есть два метода расширения на 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.

4b9b3361

Ответ 1

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

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

Что такое группа методов в С#?


Вывод метода не работает с группой методов

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

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

var x = reader.GetDoubleOrNull;

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

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

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

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 как arg для Func<string, double?>, компилятор идет "Я могу превратить GetDoubleOrNull в Func<IDataReader, string, double?>, потому что у него есть два аргумента, но я не знаю как превратить его в Func<string, double?>"

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

Ответ 2

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

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

public double? blah (строка s)

var x = reader.GetList(blah);

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

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