public static class XDecimal { public static decimal Floor( this decimal value,int precision) { decimal step = (decimal)Math.Pow(10,precision); return decimal.Floor(step * value) / step; } }
现在我尝试使用它:
(10.1234m).Floor(2)
但编译器说使用实例引用无法访问成员’decimal.Floor(decimal)’;用类型名称来限定它.我明白有静态decimal.Floor(十进制)方法.但它有不同的签名.为什么编译器无法选择正确的方法?
解决方法
你可能有一个重载分辨率的心智模型,如下所示:
>将所有可能的方法放在一个大桶中 – 扩展方法,静态方法,实例方法等.
>如果存在可能使用的错误方法,请将其从存储桶中删除.
>在其余方法中,选择与参数类型具有最佳匹配参数表达式的唯一方法.
虽然这是很多人的超载分辨率的心理模型,但遗憾的是它是巧妙的错误.
真正的模型 – 我将在这里忽略泛型类型推断问题 – 如下:
>将所有实例和静态方法放入存储桶中.虚拟覆盖不计为实例方法.
>消除不适用的方法,因为参数与参数不匹配.
在这一点上,我们要么有方法,要么我们没有.如果我们在桶中有任何方法,则不检查扩展方法.这是重要的一点.该模型不是“如果正常的重载分辨率产生错误,那么我们检查扩展方法”.该模型是“如果正常的重载分辨率没有产生任何适用的方法,那么我们检查扩展方法”.
如果存储桶中有方法,则会更多地消除基类方法,最后根据参数与参数的匹配程度选择最佳方法.
如果碰巧选择静态方法,那么C#将假设您打算使用类型名称并错误地使用实例,而不是您希望搜索扩展方法.重载分辨率已经确定存在一个实例或静态方法,其参数与您给出的参数匹配,并且它将选择其中一个或给出错误;它不会说“哦,你可能打算把这个古怪的扩展方法称为恰好在范围内”.
我知道从你的角度来看这很令人烦恼.您显然希望模型“如果重载决策产生错误,则回退到扩展方法”.在您的示例中,这将是有用的,但此行为会在其他方案中产生不良结果.例如,假设你有类似的东西
mystring.Join(foo,bar);
这里给出的错误是它应该是string.Join.如果C#编译器说“哦,string.Join是静态的,那将是奇怪的.用户可能意味着使用连接字符序列的扩展方法,让我试试……”然后你收到一条错误信息说序列连接运算符 – 这里没有任何关于你的代码的东西 – 没有正确的参数.
或者更糟糕的是,如果通过一些奇迹你确实给它的参数有效但是打算调用静态方法,那么你的代码将以一种非常奇怪且难以调试的方式被破坏.
扩展方法在游戏的最后阶段添加,并且查找它们的规则使得他们故意更喜欢给出神奇的工作错误.这是一个安全系统,以确保扩展方法不受意外的约束.