使用默认参数和泛型的方法解决问题

时间:2012-11-13 10:52:09

标签: c# .net generics overload-resolution default-parameters

使用.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所述),但是如果有更容易/更好的方式,那么我很乐意听到它。

1 个答案:

答案 0 :(得分:1)

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

来自C# 4.0 Language Specification

§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以获取更多信息。

解决方案是更改方法声明或完全删除第一个重载,然后让第二个重载:)