为什么接口上定义的C#4可选参数在实现类时没有强制执行?

the*_*onk 347 .net c# interface optional-parameters c#-4.0

我注意到,如果在接口上将参数指定为可选参数,则使用C#4中的可选参数,您不必在任何实现类上使该参数可选:

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

public class MyClass : MyInterface
{
    public void TestMethod(bool flag)
    {
        Console.WriteLine(flag);
    }
}
Run Code Online (Sandbox Code Playgroud)

因此:

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

var obj2 = new MyClass() as MyInterface;
obj2.TestMethod(); // prints false
Run Code Online (Sandbox Code Playgroud)

有谁知道为什么可选参数设计为这样工作?

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

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

Eri*_*ert 227

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

假设您有一个描述的接口,以及一百个实现它的类.然后你决定使其中一个接口的方法的参数之一可选.您是否建议正确的做法是让编译器强制开发人员找到该接口方法的每个实现,并使参数也可选?

假设我们这样做了.现在假设开发人员没有实现的源代码:


// in metadata:
public class B 
{ 
    public void TestMethod(bool b) {}
}
Run Code Online (Sandbox Code Playgroud)
// 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
Run Code Online (Sandbox Code Playgroud)

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

那不会飞.如果两个人打电话给B的作者,并且其中一个人想要默认为真,其中一个人想要它是假的呢?如果B的作者拒绝一起玩怎么办?

也许在那种情况下他们会被要求说:

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

所提出的特征似乎给程序员增加了许多不便,代表性功率没有相应的增加.这个功能的显着优势是什么证明了用户成本的增加?

  • 澄清我为什么认为它不直观的原因:对我来说,一个可选参数是"方法调用者可选",而不是"接口实现者可选". (16认同)
  • @supercat:我们没有任何计划这样做,但这样的功能是可能的.它之前已被提出过.在C#4的设计过程中,我们踢了很多我们称之为"扩展所有内容"的功能 - 我们有扩展方法,那么扩展事件,扩展属性,扩展构造函数,扩展接口等等意味着什么呢? .您可以"附加"到接口并说"这些方法是此接口的默认实现"的类是表征"扩展接口"的一种可能方式.一个有趣的想法. (9认同)
  • "你的世界是否需要在电话中打电话给B的作者并请他们向他们发送新版本[...]?" 不,不需要打电话.B应该简单地不再被认为是MyInterface的实现,并且编译器可以使D的作者知道它.D的作者需要使用接受默认参数的TestMethod来实现D,因为这是接口作者所需要的 - 您反对允许接口作者强制执行约束,因为有人可能想要破坏它.这不是一个好的论据. (8认同)
  • 我在这里同意@philofinfinitejest.界面告诉我们可以做些什么.如果我传递的接口的实现具有与接口指定的默认值不同的默认值,我怎么知道呢?嘿,我有一个默认传递true的接口,为什么这个东西会变错?那,似乎我没有得到我期望的界面.更糟糕的是,我现在必须编程实现而不是接口. (8认同)
  • @EricLippert在为编码方便而指定可选参数的情况下,是的,在实现中强制执行不便是违反直觉的.但是在使用可选参数来最小化方法重载的情况下,这是一个强大的功能,这些可选值可能具有重要意义,忽略它们肯定会削弱功率.更不用说具体实现指定与接口不同的默认值的情况.这似乎是一个非常奇怪的断开允许. (7认同)
  • 对我来说似乎仍然违反直觉.我想如果接口有一个可选参数,它将要求实现类具有相同的可选参数.由于可选参数主要与提供版本和没有该参数的版本相同,因此实现类可以选择实现两个显式版本.如果希望现有接口中的参数可选,请创建不带参数的其他版本.该类再次可以选择1个可选或2个单独.我认为更加直观. (5认同)
  • 未来.net是否有可能允许接口定义指定"默认"实现?它在概念上看起来很简单:创建一个静态泛型类,其中包含一些特殊的元数据来保存方法,当为实现接口的类构建接口vtable时,在相应的"默认方法"中填充相应的缺陷点.类.结构方法通过引用传递'this'而类方法通过值传递'this'这一事实可以通过为结构提供一个静态类而为类提供一个静态类来处理. (3认同)
  • @crush:我不会断言默认值会产生许多奇怪的情况; 我在博客上写了很多相关内容.由于这些奇怪的角落案例,C#团队拒绝将它们添加到语言中超过十年,但用户的请求 - 特别是那些想要使用专为Visual Basic设计的遗留对象模型的用户 - 既有众多且引人注目. (3认同)
  • @aaronburro:每个设计决定当然都有利弊; 如果没有,那么决定将很容易.由于存在这些问题,C#设计团队拒绝了"可选参数"功能*年*,但该功能在用户中得到了极大的要求,在处理为历史上具有此功能的语言设计的旧版对象模型时非常有用(VB),尽管你和菲尔提出了完全合理的反对意见,但设计团队决定继续这样做.这些也不是最大的反对意见!有许多艰难的决定. (2认同)
  • @EricLippert,我刚遇到的一个问题(导致我看到这篇文章)是实现类也可以选择提供不同的默认值。我实际上希望编译器不仅要求相同的默认参数,而且要求相同的默认值。如果“ MyClass”将方法定义为“ public void TestMethod(bool flag = true){...}”,则您很容易遇到意外行为。 (2认同)
  • 真正令人困惑的是`IMine mine = new Mine();。mine.Explode();`其中IMine定义了`Explode(bool destroyNearbyMines = false)`,而Mine`定义了'Explode(bool destroyNearbyMines = true)`。哪一个先例?接口的默认值还是实现?我认为实现中不应违反接口中的默认值。不过,我发现这个模棱两可。([IDEOne示例](http://ideone.com/U3qqbc)) (2认同)
  • @crush:C#使用*编译时类型*来解决编译时的重载解析问题,因此在您的示例中它将使用该接口. (2认同)
  • @sam:没有接口上的功能会破坏功能的*整点*:减轻C#开发人员的负担,他们必须使用C#可调版本的COM API,其中方法有许多可选参数.这些东西都表示为C#中的接口.这些场景是99%的原因,首先将功能添加到C#中; 当我在VSTO团队工作时,我大力游说C#团队为此添加该功能. (2认同)

Cod*_*aos 46

可选参数仅使用属性标记.此属性告诉编译器在调用站点插入该参数的默认值.

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

因此,在某种程度上,调用者始终使用可选参数提供默认值.这也会对二进制版本控制产生影响:如果更改默认值但不重新编译调用代码,它将继续使用旧的默认值.

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

如果明确实现了接口方法,则您无法做到这一点.


Olh*_*sky 30

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


小智 6

可选参数有点像我理解的宏替换.从方法的角度来看,它们并不是真正的可选项.如果您转换为接口,那么您看到的行为会导致不同的结果.