带有可选参数的接口实现
Interface implementation with optional arguments
走这个界面:
interface ILogger
{
void Store(string payload);
}
以及 ILogger
的 class 实现:
class Logger : ILogger
{
void Store(string payload, bool swallowException = true)
{
...
}
}
我预计编译器会将 swallowException
识别为可选参数,从而满足接口的要求。相反,编译器会抱怨 Logger
没有实现接口成员 Store
.
我尝试的另一件有趣的事情是显式实现接口,如下所示:
class Logger : ILogger
{
void ILogger.Store(string payload, bool swallowException = true)
{
...
}
}
编译器发出警告"The default value specified for parameter 'swallowException' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments."似乎表明可选参数在某种程度上与显式接口定义不兼容,但为什么呢?
我可以通过使用两个单独的函数定义重载 Store
来解决这个问题(可选参数存在之前的处理方式)。但是,我喜欢可选参数,因为它们的语法清晰,并且更希望它按照我期望的方式工作。
我知道可能有一个合理的(历史的或其他的)解释为什么会这样,但我似乎无法弄明白。
因为 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);
}
这避免了上述两个问题 - 接口契约得到满足,默认参数现在是被调用者的一部分,而不是调用者。
就个人而言,我根本不在 public API 方法中使用可选参数 - 当您决定在某个时候更改它们时,它们只是想引起麻烦。除非您可以确保它们 永远保持 不变,否则不要使用它们。这同样适用于 const
和 enum
- 两者也是在编译时确定的,而不是 运行-time.
记住,包含默认参数的原因是允许您不传递一些参数。这对于 COM API 调用(否则会要求您将所有不想作为 Type.Missing
传递的参数)或 null
值这样的事情有意义。当有人认为更好的默认值是 true
时,即使使用 false
也只是自找麻烦 - 突然间,一些呼叫者正在使用 true
和一些 false
,尽管所有人都认为他们正在使用 "default"。对于像您这样的情况,我会使用 bool?
,默认值为 null
(或 default(bool?)
,以您喜欢的为准)。在方法代码本身中,您可以轻松地在适当的位置处理默认值 - 例如,通过执行 swallowException.GetValueOrDefault(true)
.
走这个界面:
interface ILogger
{
void Store(string payload);
}
以及 ILogger
的 class 实现:
class Logger : ILogger
{
void Store(string payload, bool swallowException = true)
{
...
}
}
我预计编译器会将 swallowException
识别为可选参数,从而满足接口的要求。相反,编译器会抱怨 Logger
没有实现接口成员 Store
.
我尝试的另一件有趣的事情是显式实现接口,如下所示:
class Logger : ILogger
{
void ILogger.Store(string payload, bool swallowException = true)
{
...
}
}
编译器发出警告"The default value specified for parameter 'swallowException' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments."似乎表明可选参数在某种程度上与显式接口定义不兼容,但为什么呢?
我可以通过使用两个单独的函数定义重载 Store
来解决这个问题(可选参数存在之前的处理方式)。但是,我喜欢可选参数,因为它们的语法清晰,并且更希望它按照我期望的方式工作。
我知道可能有一个合理的(历史的或其他的)解释为什么会这样,但我似乎无法弄明白。
因为 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);
}
这避免了上述两个问题 - 接口契约得到满足,默认参数现在是被调用者的一部分,而不是调用者。
就个人而言,我根本不在 public API 方法中使用可选参数 - 当您决定在某个时候更改它们时,它们只是想引起麻烦。除非您可以确保它们 永远保持 不变,否则不要使用它们。这同样适用于 const
和 enum
- 两者也是在编译时确定的,而不是 运行-time.
记住,包含默认参数的原因是允许您不传递一些参数。这对于 COM API 调用(否则会要求您将所有不想作为 Type.Missing
传递的参数)或 null
值这样的事情有意义。当有人认为更好的默认值是 true
时,即使使用 false
也只是自找麻烦 - 突然间,一些呼叫者正在使用 true
和一些 false
,尽管所有人都认为他们正在使用 "default"。对于像您这样的情况,我会使用 bool?
,默认值为 null
(或 default(bool?)
,以您喜欢的为准)。在方法代码本身中,您可以轻松地在适当的位置处理默认值 - 例如,通过执行 swallowException.GetValueOrDefault(true)
.