为什么在实现类上不强制在接口上定义的C#4可选参数?


361

我注意到,使用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框架注入具体类),那么实际上就没有问题了。具有默认值的点作为调用方,无论如何都必须始终提供它。


22
因为它们是可选的?
Oded

1
但是您可以将对象实例转换为MyInterface,并使用可选参数调用它 ((MyInterface)obj).TestMethod();
Jim Mischel

7
@oded-但是,如果您说此参数在合同中是可选的,为什么您不允许实现者将其设置为可选?这是否会给想要使用合同的人造成混乱?
theburningmonk 2011年

1
我认为在这种情况下,您可以说该参数在实现中是可选的,而不是在调用实现方法时。当您在类中调用该方法时,必须遵循类规则(该参数在该类中不是可选的,因此您可以(在没有接口的情况下,不要调用该方法),并且在第二种方法中,实现接口时必须遵循接口规则,因此您可以使用/不使用可选参数来覆盖方法。只是一个意见。
Mohamad Alhamoud 2011年

Answers:


235

更新:这个问题是我2011年5月12日博客的主题。感谢您提出的伟大问题!

假设您有一个描述的接口,以及一百个实现该接口的类。然后,您决定将接口方法之一的参数设为可选。您是否在建议正确的事情是让编译器强迫开发人员查找该接口方法的每个实现,并使参数也可选?

假设我们做到了。现在,假设开发人员没有实现的源代码:


// in metadata:
public class B 
{ 
    public void TestMethod(bool b) {}
}

// in source code
interface MyInterface 
{ 
    void TestMethod(bool b = false); 
}
class D : B, MyInterface {}
// Legal because D's base class has a public method 
// that implements the interface method

D的作者应该如何进行这项工作?在您的世界中,他们是否需要打电话给B的作者,并要求他们为他们提供新版本的B,使该方法具有可选参数?

那不会飞。如果两个人打电话给B的作者,而其中一个人希望默认值是true,而其中一个人希望它是false,该怎么办呢?如果B的作者只是拒绝一起玩怎么办?

也许在这种情况下,他们将被要求说:

class D : B, MyInterface 
{
    public new void TestMethod(bool b = false)
    {
        base.TestMethod(b);
    }
}

拟议的功能似乎给程序员增加了很多不便,而代表能力没有相应增加。此功能的显着优势是什么,可以证明用户增加了成本?


更新:在下面的评论中,supercat提出了一种语言功能,它将真正增加语言的功能并启用某些类似于此问题中描述的场景的方案。仅供参考,该功能-接口中方法的默认实现-将添加到C#8中。


9
@supercat:我们没有任何计划,但是可以使用类似功能。以前有人提出过。在C#4的设计过程中,我们引入了许多所谓的“扩展所有内容”功能-我们具有扩展方法,因此具有扩展事件,扩展属性,扩展构造函数,扩展接口等意味着什么。 。可以“附加”到接口并说“这些方法是该接口的默认实现”的类是表征“扩展接口”的一种可能方法。一个有趣的想法。
埃里克·利珀特

16
为了阐明我认为它不直观的原因:对我来说,可选参数是“方法调用者可选的”而不是“接口实现者可选的”。
Stefan de Kok

8
“在您的世界中,他们是否需要打电话给B的作者,并要求他们为他们提供新版本?[...]?不,不需要打来电话。B不再应该仅仅被视为MyInterface的实现,并且编译器可以使D的作者意识到这一点。D的作者可能需要使用TestMethod来实现D,该TestMethod接受一个默认参数,因为这是Interface作者所需要的-您在争论是否允许接口作者强制执行约束,因为有人可能想打破它。那不是一个好论点。
philofinfinitejest

7
@EricLippert在为编码方便而指定了可选参数的情况下,是的,在实现中实施不便是违反直觉的。但是,在使用可选参数来最大程度地减少方法重载这一强大功能的情况下,那些可选值可能会发挥重要作用,而忽略它们肯定会削弱这种能力。更不用说具体实现指定与接口不同的默认值的情况。这似乎是一个非常奇怪的断开连接。
philofinfinitejest

8
我在这里同意@philofinfinitejest。一个界面告诉您可以做什么。如果我通过的接口实现的默认值不同于接口指定的默认值,我怎么知道呢?嗨,我有一个默认传递true的接口,那么为什么这东西会变成false?那,好像我没有得到我期望的接口。更糟糕的是,我现在必须编程实现而不是接口。
2014年

47

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

当C#代码被编译为IL时而不是在JIT时,该调用将obj2.TestMethod();被替换obj2.TestMethod(false);

因此,从某种意义上说,始终是调用方提供带有可选参数的默认值。这也会对二进制版本控制产生影响:如果您更改默认值但不重新编译调用代码,它将继续使用旧的默认值。

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

如果接口方法是显式实现的,那么您已经无法做到。


1
您是否可以强制实施者显式实施?
2014年

30

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


7

根据我的理解,可选参数有点像是宏替换。从方法的角度来看,它们并不是真正可选的。这是工件的一种行为,如果您将其转换为接口,则会看到不同的结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.