12

使用 .NET 4,我对编译器无法解析下面示例中的第一个方法调用感到困惑。

using System;

namespace MethodResolutionTest
{
    class Program
    {
        static void Main(string[] args)
        {
            NonGeneric foo = null;

            // ambiguous
            foo.Ext1(x => new NonGeneric());

            // resolves to first Ext1
            foo.Ext1(x => new NonGeneric(), 1);


            // resolves to first Ext2
            foo.Ext2(x => new NonGeneric());

            // resolves to first Ext2
            foo.Ext2(x => new NonGeneric(), 1);

            // resolves to second Ext2
            foo.Ext2(x => "foo");

            // resolves to second Ext2
            foo.Ext2(x => "foo", 1);


            // resolves to first Ext3
            foo.Ext3(x => new NonGeneric());

            // resolves to first Ext3
            foo.Ext3(x => new NonGeneric(), 1);

            // resolves to second Ext3
            foo.Ext3(x => "foo");

            // resolves to second Ext3
            foo.Ext3(x => "foo", 1);
        }
    }

    public class NonGeneric
    {
    }

    public class Generic<T> : NonGeneric
    {
    }

    public static class Extensions1
    {
        public static NonGeneric Ext1(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0)
        {
            return null;
        }

        public static Generic<TNext> Ext1<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0, string s = null)
        {
            return null;
        }
    }

    // only difference between Extensions2 and Extensions1 is that the second overload no longer has a default string parameter
    public static class Extensions2
    {
        public static NonGeneric Ext2(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0)
        {
            return null;
        }

        public static Generic<TNext> Ext2<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0)
        {
            return null;
        }
    }

    // Extensions3 explicitly defines an overload that does not default the int parameter
    public static class Extensions3
    {
        public static NonGeneric Ext3(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext)
        {
            return Ext3(first, getNext, default(int));
        }

        public static NonGeneric Ext3(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0)
        {
            return null;
        }

        public static Generic<TNext> Ext3<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0)
        {
            return null;
        }
    }
}

任何人都可以对此有所了解吗?我怀疑除了修改我的 API 以帮助编译器(如上所述)之外,我真的没有前进的道路Extensions3,但如果有更简单/更好的方法,那么我很想听听。

4

1 回答 1

1

这是模棱两可的,因为您在第二个Ext1扩展方法中有两个可选参数。因为在第一次调用时这两个参数都被省略了,所以编译器不知道你想使用哪个。

来自C# 4.0 语言规范

§7.5.3 重载分辨率

给定一组适用的候选函数成员,找到该集合中的最佳函数成员。如果该集合仅包含一个函数成员,则该函数成员是最佳函数成员。否则,最好的函数成员是在给定参数列表方面优于所有其他函数成员的一个函数成员,前提是使用第 7.5.3.2 节中的规则将每个函数成员与所有其他函数成员进行比较。如果没有一个函数成员比所有其他函数成员更好,那么函数成员调用是不明确的,并且会发生绑定时错误。

此外,根据§7.5.3.2 更好的功能成员

从参数列表中删除没有对应参数的可选参数

这意味着,当您省略方法调用中的最后两个参数并NonGeneric推断类型时(请阅读 §7.5.2 下的类型推断),两种方法都将如下所示:

Ext1(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext)

因此,它们将是模棱两可的......

我建议阅读规范的 §7.5.3.2 甚至整个 §7.5.3 以获取更多信息。

解决方案是更改您的方法声明或完全删除第一个重载并让第二个完成工作:)

于 2012-11-13T11:08:17.407 回答