我在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
时的工作方式。
答案 0 :(得分:8)
前言:如果您想要完整的说明,请跳至编辑。 Spoiler:扩展方法是一种编译器技巧,实际上还有一个参数比你调用它们时的样子。
它是方法组的解决方案。显然C#编译器没有花时间弄清楚你使用的方法是否有重载;它总是需要一个明确的演员。退房:
What is a method group in C#?
Method Inference does not work with method group
从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);
它会正确编译。是什么给了什么?
以下是答案:扩展方法不会实际向对象添加方法。它们实际上是一个编译器技巧,允许您编程好像这些方法是你班级的一部分。来自here:
在您的代码中,您使用实例方法调用扩展方法 句法。但是,由中间语言(IL)生成 编译器将您的代码转换为静态方法的调用。 因此,封装原理并非如此 侵犯。实际上,扩展方法无法访问私有变量 他们正在扩展的类型。
所以,当你打电话
var x = reader.GetDoubleOrNull("myColumnName");
实际上正在编译和执行的内容基本上就是这个(完全合法的调用,即使它是一种扩展方法):
var x = Extensions.GetDoubleOrNull(reader, "myColumnName");
因此,当您尝试使用GetDoubleOrNull
作为Func<string, double?>
的arg时,编译器将进入&#34;我可以将GetDoubleOrNull
转换为Func<IDataReader, string, double?>
,因为它有两个论点,但我不知道如何把它变成Func<string, double?>
&#34;
即使您将其称为IDataReader
的实例方法,只有一个arg,但它不是:它只是C#编译器欺骗你思考的两个args的静态方法是IDataReader
的一部分。
答案 1 :(得分:0)
GetDoubleOrNull返回一个double?
GetList需要IDataReader和System.Func<string,int?>
错误消息有点误导
公共双重? blah(string s)var x = reader.GetList(blah);
blah是本次电话会议的代表。在GetList(GetDoubleOrNull)中,它是get doubleOrNull的结果。
虽然好问题。如果我帮助与否,请发布答案。