隐式强制转换对委托类型推断的意外影响

我有一个简单的Money类型,带有decimal的隐式decimal

 struct Money { decimal innerValue; public static implicit operator Money(decimal value) { return new Money { innerValue = value }; } public static explicit operator decimal(Money value) { return value.innerValue; } public static Money Parse(string s) { return decimal.Parse(s); } } 

我定义了一个Sum()重载来操作这些值:

 static class MoneyExtensions { public static Money Sum(this IEnumerable source, Func selector) { return source.Select(x => (decimal)selector(x)).Sum(); } } 

我没想到的是这个扩展方法干扰了现有的Sum()扩展方法:

 var source = new[] { "2" }; Money thisWorks = source.Sum(x => Money.Parse(x)); int thisWorksToo = source.Sum(new Func(x => int.Parse(x))); int thisDoesNot = source.Sum(x => int.Parse(x)); 

错误是“无法将类型’Money’隐式转换为’int’。存在显式转换(您是否错过了转换?)”。 编译器是否支持int => decimal => Money隐式转换而不是解决完全匹配的重载?

从C#4.0规范,第7.6.5.2节:

前面的规则意味着实例方法优先于扩展方法,内部命名空间声明中可用的扩展方法优先于外部命名空间声明中可用的扩展方法,并且直接在命名空间中声明的扩展方法优先于导入到该命名空间中的扩展方法。带有using namespace指令的命名空间

可能这会导致您的Money Sum扩展方法优先于Linq的方法 – 这就是为什么您没有得到“模糊方法调用”错误的原因。

继Rob Siklos的研究之后(请将研究报告投票)将扩展名放在一个单独的命名空间中可以解决这个问题。 我似乎记得这是扩展指南之一。

 using System; using System.Collections.Generic; using System.Linq; using Extensions; namespace Currency { struct Money { decimal innerValue; public static implicit operator Money(decimal value) { return new Money { innerValue = value }; } public static explicit operator decimal(Money value) { return value.innerValue; } public static Money Parse(string s) { return decimal.Parse(s); } } class Program { static void Main() { var source = new[] { "2" }; Money thisWorks = source.Sum(x => Money.Parse(x)); int thisWorksToo = source.Sum(new Func(x => int.Parse(x))); int thisWorksTooNow = source.Sum(x => int.Parse(x)); } } } namespace Extensions { static class IEnumerableTExtensions { public static Currency.Money Sum( this IEnumerable source, Func selector) { return source.Select(x => (decimal)selector(x)).Sum(); } } } 

这是因为您明确声明thisDoesNotint类型。 如果你使用隐式声明,它工作正常:

 void Main() { var source = new[] { "2" }; Money thisWorks = source.Sum(x => Money.Parse(x)); int thisWorksToo = source.Sum(new Func(x => int.Parse(x))); var thisDoesNot = source.Sum(x => int.Parse(x)); Console.Write(thisDoesNot.GetType()); } 

从规格 :

方法组标识要调用的一个方法或从中选择要调用的特定方法的重载方法集。 在后一种情况下,确定要调用的特定方法是基于参数列表中参数类型提供的上下文