Skip to content

C# 可选参数和方法重载同时使用造成的困惑

C# 中可选参数是一个很方便的功能,但是在和重载同时使用时发现了一个比较困惑的地方。

比如下面例子中的前两个方法,第二个方法在可选参数不传的情况下,其调用的方式是和第一个方法一样的。

本以为编译器在编译时会报错,但实际并非如此。

在下面的例子中定义了 7 个方法定义,只有两种认为是参数类型重复了。

也就是说 方法定义是否重复的认定是不管参数是不是可选参数,也不管方法的返回值类型是否相同,只按照参数的顺序和类型来判定。

OverloadTest.cs

csharp
class OverloadTest
{
    public void Method1(string arg1)
    {
        Console.WriteLine("Method1 - 1");
    }

    public void Method1(string arg1, string arg2 = "")
    {
        Console.WriteLine("Method1 - 2");
    }

    public void Method1(string arg1, string arg2 = "", string arg3  = "")
    {
        Console.WriteLine("Method1 - 3");
    }

    public void Method1(string arg1, string arg2 = "", int arg3 = 0)
    {
        Console.WriteLine("Method1 - 4");
    }

    public void Method1(string arg1, int arg3 = 0, string arg2 = "")
    {
        Console.WriteLine("Method1 - 5");
    }

    //// 编译报错:类型“OverloadTest”已定义了一个名为“Method1”的具有相同参数类型的成员
    //public string Method1(string arg1)
    //{
    //    return arg1;
    //}

    //// 编译报错:类型“OverloadTest”已定义了一个名为“Method1”的具有相同参数类型的成员
    //public void Method1(string arg1, int arg2 = 0, string arg3 = "")
    //{
    //}
}

下面是这几个方法的调用,比较有意思的是最后编译出错的两个。

本意是调用打印 Method1 - 4Method1 - 5 的两个方法的,但是在使用 命名实参 进行传递时编译报错了。

csharp
var test = new OverloadTest();

test.Method1("1"); // Method1 - 1

test.Method1("1", "2"); // Method1 - 2
test.Method1("1", "2", "3"); // Method1 - 3
test.Method1("1", "2", 3); // Method1 - 4
test.Method1("1", 2, "3"); // Method1 - 5

test.Method1("1", arg2: "2"); // Method1 - 2(其实对方法 2 3 4 5 都符合)
test.Method1("1", arg2: "2", arg3: "3"); // Method1 - 3
// 编译报错:以下方法或属性之间的调用具有二义性:“OverloadTest.Method1(string, string, int)”和“OverloadTest.Method1(string, int, string)”
//test.Method1("1", arg2: "2", arg3: 3);
// 编译报错:以下方法或属性之间的调用具有二义性:“OverloadTest.Method1(string, string, int)”和“OverloadTest.Method1(string, int, string)”
//test.Method1("1", arg3: 2, arg2: "3");

说来这个也许就是 java 方法为什么没有可选参数的原因,可能会导致方法调用时的二义性。

又想到两种通过 命名实参 方式的调用:

csharp
test.Method1("1", arg3: "3"); // Method1 - 3(仅符合方法 3 的定义)
// 编译报错:以下方法或属性之间的调用具有二义性:“OverloadTest.Method1(string, string, int)”和“OverloadTest.Method1(string, int, string)”
//test.Method1("1", arg3: 3); // Method1 - 3

总的来说感觉如果带可选参数的重载多了之后,调用感觉很混乱,建议不要两种方法同时使用,如果同时使用,应避免出现方法签名覆盖的情况出现