Mos*_*oss 13 c# generics extension-methods delegates internals
我在IDataReader上有两个扩展方法,具有以下签名:
internal static List<T> GetList<T>(this IDataReader reader, Func<string, T> del)
internal static double? GetDoubleOrNull(this IDataReader reader, string columnName)
Run Code Online (Sandbox Code Playgroud)
GetDoubleOrNull 没有任何重载.
在其他地方,我能做到
Func<string, double?> del = reader.GetDoubleOrNull;
var x = reader.GetList(del);
Run Code Online (Sandbox Code Playgroud)
要么
var x = reader.GetList<double?>(reader.GetDoubleOrNull);
Run Code Online (Sandbox Code Playgroud)
或者只是传入一个类似的实例方法
public double? blah(string s)
var x = reader.GetList(blah);
Run Code Online (Sandbox Code Playgroud)
但我不能这样做
var x = reader.GetList(reader.GetDoubleOrNull);
Run Code Online (Sandbox Code Playgroud)
编译器给出错误
cannot convert from 'method group' to 'System.Func<string,double?>'
Run Code Online (Sandbox Code Playgroud)
我不明白这一点.我认为既然没有重载GetDoubleOrNull,就不会有重载决策,它可以从方法签名推断出类型参数.
真正令人困惑的部分是它在传入时的工作方式blah.
前言:如果需要完整说明,请跳至编辑.Spoiler:扩展方法是一种编译器技巧,实际上还有一个参数比它们调用它们时的样子.
它在方法组的分辨率中.显然C#编译器没有花时间弄清楚你使用的方法是否有重载; 它总是需要一个明确的演员.查看:
返回的方法组reader.GetDoubleOrNull通过您尝试将其强制转换为缩小范围:GetDoubleOrNull可以引用具有该名称的任意数量的重载方法.你必须明确地施展它.
有趣的是,出于同样的原因,您甚至无法将方法组分配给隐式类型变量:
var x = reader.GetDoubleOrNull;
Run Code Online (Sandbox Code Playgroud)
无法编译,因为它需要显式转换.
编辑我很确定这里的混淆与扩展方法有关:
查看以下测试类:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
你可以成功打电话
var x = reader.GetList(Extensions.blah);
Run Code Online (Sandbox Code Playgroud)
为什么会这样?blah也是一种静态扩展方法,因此,根据您的证据,看起来上面的行不应该编译.更复杂的是,让我们添加这个方法:
public static List<T> GetList2<T>(this IDataReader reader, Func<IDataReader, string, T> del)
{
throw new NotImplementedException();
}
Run Code Online (Sandbox Code Playgroud)
你现在可以打电话了
x = reader.GetList2(Extensions.GetDoubleOrNull);
Run Code Online (Sandbox Code Playgroud)
它会正确编译.是什么赋予了?
答案如下:扩展方法实际上并没有向对象添加方法.它们真的是一个编译器技巧,允许你编程,好像这些方法是你的类的一部分.从这里:
在您的代码中,您使用实例方法语法调用扩展方法.但是,编译器生成的中间语言(IL)会将您的代码转换为静态方法的调用.因此,封装原则并未真正被违反.实际上,扩展方法无法访问它们正在扩展的类型中的私有变量.
所以,当你打电话的时候
var x = reader.GetDoubleOrNull("myColumnName");
Run Code Online (Sandbox Code Playgroud)
实际上正在编译和执行的内容基本上是这个(一个完全合法的调用,即使它是一个扩展方法):
var x = Extensions.GetDoubleOrNull(reader, "myColumnName");
Run Code Online (Sandbox Code Playgroud)
因此,当你尝试使用GetDoubleOrNullarg作为a时Func<string, double?>,编译器会"我可以GetDoubleOrNull变成a,Func<IDataReader, string, double?>因为它有两个参数,但我不知道如何把它变成一个Func<string, double?>"
即使你把它称为IDataReader带有一个arg 的实例方法,它也不是:它只是一个带有两个args的静态方法,C#编译器欺骗你思考是其中的一部分IDataReader.
GetDoubleOrNull 返回双精度值?GetList 需要一个 IDataReader 和一个System.Func<string,int?>
错误消息有点误导
公共双?等等(字符串 s)
var x = reader.GetList(blah);
blah 是这次电话会议的代表。在 GetList(GetDoubleOrNull) 中,它是 get doubleOrNull 的结果。
不过,好问题。无论我是否有帮助,请务必发布答案。