【问题标题】:Interface implementation with optional arguments带有可选参数的接口实现
【发布时间】:2015-05-07 15:30:14
【问题描述】:

取这个接口:

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 来解决这个问题(可选参数存在之前的处理方式)。不过,我喜欢可选参数,因为它们的语法清晰,并且希望这能按我预期的方式工作。

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

【问题讨论】:

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


【解决方案1】:

因为 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 时自找麻烦——突然间,一些调用者使用true 和一些false,尽管所有人都认为他们使用的是“默认值”。对于像你这样的情况,我会改用bool?,默认值为null(或default(bool?),随你喜欢)。在方法代码本身中,您可以轻松地在适当的位置处理默认值 - 例如,通过执行 swallowException.GetValueOrDefault(true)

【讨论】:

  • 我想我现在更好地理解了可选参数的机制。我明白你所说的需要完全匹配接口的显式接口定义是什么意思。我应该意识到在这里使用可选参数是没有意义的。我的错。
  • 抛开明确的情况,编译器不能代表我发出两个函数定义吗?换句话说,编译器是否执行您建议我“手动”执行的相同操作?我认为这将消除您所描述的调用者使用过时默认值编译的问题。是否有一些我没有想到的逻辑原因这不起作用?
  • 无论如何,考虑到可选参数现在的工作方式,我想我一定会听从你的建议,而不是在公共方法中使用可选参数......我很开明!谢谢。
  • @JamesJones 当然可以。但这不是可选参数的工作方式:) 我假设他们希望避免处理这些方法的重载 - 只有一个重载,具有正确数量(和类型)的参数。如果它们为每个生成一个重载,则可能与该类型上定义的其他方法发生冲突。通常,当你想做这种“默认”时,它还涉及对输入的一些(简单)操作,所以它不会那么有用。重要的是 - 命名参数。在选择要传递的参数时,您可以选择忽略所有默认参数。
  • @JamesJones 命名参数也可能是最强的参数。同样,想想 COM 方法调用,您有一个具有 30 个参数的方法(曾经使用 Excel API?:)),您只想传递其中三个,其余的是 Type.Missing。如果默认参数只是确保方法有 30 个重载,您将无法挑选要传递的参数 - 它们总是必须按顺序排列。
猜你喜欢
  • 1970-01-01
  • 2022-08-06
  • 1970-01-01
  • 2019-07-18
  • 1970-01-01
  • 2023-03-17
  • 2021-11-22
  • 2013-06-07
  • 1970-01-01
相关资源
最近更新 更多