我注意到在c# 4中的可选参数中,如果你在接口上指定了一个可选参数,你不必在任何实现类上使该参数为可选:

public interface MyInterface
{
    void TestMethod(bool flag = false);
}

public class MyClass : MyInterface
{
    public void TestMethod(bool flag)
    {
        Console.WriteLine(flag);
    }
}

因此:

var obj = new MyClass();        
obj.TestMethod(); // compiler error

var obj2 = new MyClass() as MyInterface;
obj2.TestMethod(); // prints false

有人知道为什么可选参数是这样设计的吗?

一方面,我认为覆盖接口上指定的任何默认值的能力是有用的,但老实说,我不确定你是否应该能够在接口上指定默认值,因为这应该是一个实现决策。

另一方面,这种断开意味着您不能总是交替使用具体类和接口。当然,如果在实现中指定了默认值,这就不是问题,但是如果您将具体类公开为接口(例如使用一些IOC框架注入具体类),那么使用默认值就没有意义了,因为调用者无论如何都必须提供它。


当前回答

可选参数只是用属性标记。此属性告诉编译器在调用点插入该参数的默认值。

调用obj2.TestMethod();替换为obj2.TestMethod(false);当c#代码被编译为IL时,而不是在jit时。

所以在某种程度上,它总是调用者提供默认值和可选参数。这对二进制版本控制也有影响:如果您更改了默认值,但不重新编译调用代码,它将继续使用旧的默认值。

另一方面,这种断开意味着您不能总是交替使用具体类和接口。

如果接口方法是显式实现的,那么您已经不能这样做了。

其他回答

谢谢你的解释@eric-lippert

下面是一些代码示例:

[Fact]
public void TestOptionalMethodArgument()
{
    var implementation = new TestHello();
    IHello @interface = implementation;

    Assert.Equal(23, @interface.Action());
    Assert.Equal(40, implementation.Action());
}

public class TestHello : IHello
{
    public int Action(int number = 40)
        => number;
}

public interface IHello
{
    int Action(int number = 23);
}

因为默认参数是在编译时而不是运行时解析的。 所以默认值不属于被调用的对象,而是属于被调用的引用类型。

var obj = new MyClass();        
obj.TestMethod(); // compiler error

var obj2 = new MyClass() as MyInterface;
obj2.TestMethod(); // prints false

Use

MyInterface obj = new MyClass();        
obj.TestMethod(); // compiler error

var obj2 = new MyClass() as MyInterface;
obj2.TestMethod(); // prints false

结果都是假的

可选参数只是用属性标记。此属性告诉编译器在调用点插入该参数的默认值。

调用obj2.TestMethod();替换为obj2.TestMethod(false);当c#代码被编译为IL时,而不是在jit时。

所以在某种程度上,它总是调用者提供默认值和可选参数。这对二进制版本控制也有影响:如果您更改了默认值,但不重新编译调用代码,它将继续使用旧的默认值。

另一方面,这种断开意味着您不能总是交替使用具体类和接口。

如果接口方法是显式实现的,那么您已经不能这样做了。

从我的理解来看,可选参数有点像宏替换。从方法的角度来看,它们实际上不是可选的。它的一个产物是您看到的行为,如果您强制转换到一个接口,您将得到不同的结果。