简体   繁体   English

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

[英]Unexpected effect of implicit cast on delegate type inference

I have a simple Money type with an implicit cast from decimal :我有一个简单的Money类型,带有从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);
    }
}

And I defined a Sum() overload to operate on those values:我定义了一个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();
    }
}

What I didn't expect was for this extension method to interfere with the existing Sum() extension methods:我没想到的是这个扩展方法会干扰现有的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));

The error is "Cannot implicitly convert type 'Money' to 'int'. An explicit conversion exists (are you missing a cast?)".错误是“无法将类型 'Money' 隐式转换为 'int'。存在显式转换(您是否缺少演员表?)”。 Is it correct that the compiler favors int => decimal => Money implicit conversions over resolving an overload that's an exact match?编译器偏爱int => decimal => Money隐式转换而不是解决完全匹配的重载是否正确?

From the C# 4.0 Specification, section 7.6.5.2:来自 C# 4.0 规范,第 7.6.5.2 节:

The preceding rules mean that instance methods take precedence over extension methods, that extension methods available in inner namespace declarations take precedence over extension methods available in outer namespace declarations, and that extension methods declared directly in a namespace take precedence over extension methods imported into that same namespace with a using namespace directive前面的规则意味着实例方法优先于扩展方法,内部命名空间声明中可用的扩展方法优先于外部命名空间声明中可用的扩展方法,并且直接在命名空间中声明的扩展方法优先于导入到同一命名空间中的扩展方法带有 using 命名空间指令的命名空间

Probably, this is causing your Money Sum extension method to take precedence over the ones from Linq - that's why you don't get an "ambiguous method call" error.可能这会导致您的 Money Sum 扩展方法优先于 Linq 中的扩展方法 - 这就是为什么您不会收到“模糊方法调用”错误的原因。

Following on from Rob Siklos's research, (please vote up the research) Putting the extension in a seperate namespace fixes this problem.继 Rob Siklos 的研究之后,(请投票支持该研究)将扩展放在单独的命名空间中解决了这个问题。 I seem to recall this as one of the guidelines for extensions.我似乎记得这是扩展指南之一。

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();
        }
    }
}

It's because you are explicitly declaring thisDoesNot as type int .这是因为您将thisDoesNot明确声明为int类型。 If you use implicit declaration, it works fine:如果您使用隐式声明,它可以正常工作:

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());
}

From the specification :规范

The method group identifies the one method to invoke or the set of overloaded methods from which to choose a specific method to invoke.方法组标识要调用的一个方法或一组重载方法,从中选择要调用的特定方法。 In the latter case, determination of the specific method to invoke is based on the context provided by the types of the arguments in the argument-list .在后一种情况下,要调用的具体方法的确定基于参数列表中 arguments 的类型提供的上下文

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM