使用可选参数的接口实现

时间:2015-05-07 15:30:14

标签: c# .net .net-4.5 c#-5.0

采取这个界面:

interface ILogger
{
    void Store(string payload);
}

ILogger的这个类实现:

class Logger : ILogger
{
    void Store(string payload, bool swallowException = true)
    {
        ...
    }
}

我预计编译器会将swallowException识别为可选参数,从而满足接口的要求。相反,发生的事情是编译器抱怨Logger没有实现接口成员Store

我尝试的另一个有趣的事情是明确地实现接口,如下所示:

class Logger : ILogger
{
    void ILogger.Store(string payload, bool swallowException = true)
    {
        ...
    }
}

编译器发出警告“为参数'swallowException指定的默认值'将不起作用,因为它适用于在不允许可选参数的上下文中使用的成员。”它似乎暗示可选参数在某种程度上与显式接口定义不兼容,但为什么?

我可以通过使用两个单独的函数定义(在存在可选参数之前执行操作的方式)重载Store来解决此问题。但是我喜欢它们语法清晰度的可选参数,并且希望这只是按照我期望的方式工作。

我理解可能有一个合理的(历史的或其他的)解释为什么会这样,但我似乎无法弄明白。

1 个答案:

答案 0 :(得分:16)

因为C#中的可选参数只是语法糖。

您的案例中的方法定义是

void Store(string payload, bool swallowException)

而不是

void Store(string payload)

这显然与界面不匹配。

默认参数的工作方式是编译器将默认值注入方法的调用中。因此,如果执行Store(payload),编译器将实际发出Store(payload, true)。这对于理解默认参数非常重要 - 它是在调用者的编译时完成的。因此,如果您在不重新编译调用者的情况下更改被调用者中的默认参数,则调用者仍将使用旧的默认参数。

这也解释了你得到的警告 - 因为默认值是由编译器显式传递的,并且你不能在不转换为接口的情况下调用接口的显式实现,你将不会有机会永远使用默认值。

您根本不想使用默认参数。只需定义两个方法:

void Store(string payload, bool swallowException)
{
  // Do your job
}

void Store(string payload)
{
  Store(payload, true);
}

这避免了上述两个问题 - 接口契约得到满足,默认参数现在是被调用者的一部分,而不是调用者。

就个人而言,我根本不在公共API方法中使用可选参数 - 当你决定在某些时候想要改变它们时,它们只会引起麻烦。除非你能确保他们永远保持相同,否则不要使用它们。这同样适用于constenum - 两者也在编译时确定,而不是在运行时确定。

请记住,包含默认参数的原因是允许您不传递某些参数。这对于COM API调用(否则会要求您传递您不想传递的所有参数Type.Missing)或null值这样的事情是有意义的。即使使用false,当有人决定更好的默认设置为true时,即使有些问候人员正在使用truefalse,但即使使用bool?也是如此。 '使用“默认”。对于像您这样的情况,我会改为使用null,默认值为default(bool?)(或swallowException.GetValueOrDefault(true),无论您喜欢哪种方式)。在方法代码本身中,您可以在适当的位置轻松处理默认值 - 例如,通过执行$company = $_POST['company_name'];