2011-06-30 85 views
9

我有从decimal简单Money类型与隐式转换:意想不到的效果

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<TSource>(this IEnumerable<TSource> source, Func<TSource, Money> 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<string, int>(x => int.Parse(x))); 
int thisDoesNot = source.Sum(x => int.Parse(x)); 

错误是“不能隐式地将类型'Money'转换为'int'。一个显式转换存在(是否缺少强制转换?)”。它是正确的,因为编译器在解决超载这是一个精确匹配有利于int => decimal => Money隐式转换?

+0

我错过了完全匹配 – Jodrell

+0

System.Linq中'Sum()'的int'重载。 – dahlbyk

+0

你能提供一个完整的例子来产生编译错误吗?你没有展示你的“使用”声明,这在这种情况下非常重要。 –

回答

8

从C#4.0规范,部分7.6.5.2:在可用的扩展方法

前述规则意味着实例方法优先于扩展方法,在内部命名空间声明可用的扩展方法优先在外部命名空间声明,并直接在命名空间中宣布扩展方法优先于导入到同一个命名空间与使用namespace指令

可能扩展方法,这导致您的Money Sum扩展方法优先于来自Linq的扩展方法 - 这就是为什么您不会收到“模糊方法调用”错误。

+0

所以它应该工作,如果我将我的'Sum()'方法移动到不同的命名空间? – dahlbyk

+0

@dahlbyk,是的,这是有效的我试过了,很好的回答,+ 1 – Jodrell

+0

@dahlbyk:是的 - 如果扩展方法类的根名称空间与您的测试代码的执行位置不同,它应该可以工作。 – RobSiklos

-1

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

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

    Console.Write(thisDoesNot.GetType()); 
} 

specification

的方法,组确定一个 方法来调用或者从 重载的方法集合中选择一个特定的方法调用。 在 后面的情况下,决定调用的具体方法是基于 提供的上下文类型 参数列表中的参数

+0

它编译的一部分,但'thisDoesNot'没有键入'Money'而是'int'。在真实场景中,我有一个包含'Money Price'和'int Quantity'的物品的购物车 - ''Quantity'上的Sum()'返回'int'无法编译。 – dahlbyk

+0

哦,我以为你想要它是类型钱。我看到...... – scottm

+0

该规范适用于VS 2003,在扩展方法出现之前。 – RobSiklos

2

从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<string, int>(x => int.Parse(x)));  
      int thisWorksTooNow = source.Sum(x => int.Parse(x)); 

     } 
    } 
} 
namespace Extensions 
{ 
    static class IEnumerableTExtensions 
    { 
     public static Currency.Money Sum<TSource>(
             this IEnumerable<TSource> source, 
             Func<TSource, Currency.Money> selector) 
     { 
      return source.Select(x => (decimal)selector(x)).Sum(); 
     } 
    } 
} 
相关问题