此代码将无法编译:
using System; using System.Runtime.CompilerServices; static class Extensions { public static void Foo(this A a, Exception e = null, string memberName = "") { } public static void Foo(this A a, T t, Exception e = null, string memberName = "") where T : class, IB { } } interface IB { } class A { } class Program { public static void Main() { var a = new A(); var e = new Exception(); a.Foo(e); //<- Compile error "ambiguous call" } }
但如果我删除最后一个string
参数一切都很好:
public static void Foo(this A a, Exception e = null) { } public static void Foo(this A a, T t, Exception e = null) where T : class, IB { }
问题是 - 为什么这些可选string
参数会破坏编译器对方法调用的选择?
补充: 明确的问题:我不明白为什么编译器不能在第一种情况下选择正确的重载但是可以在第二种情况下进行?
编辑:
[CallerMemberName]
属性不是问题的原因所以我已经从问题中删除它.
@PetSerAl已经在评论中指出了规范,但是让我把它翻译成简单的英语:
C#语言有一条规则,即在没有省略默认参数的情况下,重载优先于带有省略的默认参数的重载.这个规则Foo(this A a, Exception e = null)
比一个更好的匹配Foo(this A a, T t, Exception e = null)
.
C#语言没有规则说,带有一个省略的默认参数的重载优先于具有两个省略的默认参数的重载.因为它没有这样的规则,Foo(this A a, Exception e = null, string s = "")
所以不是优先考虑的Foo
.
避免此问题的最简单方法通常是提供额外的重载,而不是使用默认参数值.您需要默认参数值CallerMemberName
才能工作,但是您可以提供额外的重载来省略Exception
,并通过传递null
来转发到实际实现.
注意:确保Foo
在Foo(this A a, Exception e, string s = "")
可用时不会被挑选将是一个棘手的问题,无论如何.如果你的变量是静态类型的Exception
,那么非泛型方法将是首选,但如果它是静态类型的,例如ArgumentException
,那么T=ArgumentException
是一个比基类更好的匹配Exception
,并且T=ArgumentException
将检测到错误太晚,无法选择方法你想打电话.放置T
之后 可能是最安全的Exception
,并且总是需要在(可能null
)传递异常时使用泛型方法.