And*_*ndi 6 c# c#-8.0 default-interface-member
C#8支持接口中的默认方法实现。我的想法是将一个日志记录方法注入这样的类中:
public interface ILoggable {
void Log(string message) => DoSomethingWith(message);
}
public class MyClass : ILoggable {
void MyMethod() {
Log("Using injected logging"); // COMPILER ERROR
}
}
Run Code Online (Sandbox Code Playgroud)
我收到一个编译器错误:“名称在当前上下文中不存在”
以这种方式使用默认方法实现是不可能的吗?
编辑:
请参阅https://docs.microsoft.com/zh-cn/dotnet/csharp/tutorials/default-interface-members-versions中的文档
从
SampleCustomer到的转换ICustomer是必要的。本SampleCustomer类并不需要提供一个实现ComputeLoyaltyDiscount; 由ICustomer界面提供。但是,SampleCustomer该类不会从其接口继承成员。该规则没有改变。为了调用在接口中声明和实现的任何方法,ICustomer在此示例中,变量必须是接口的类型。
所以方法就像
public class MyClass : ILoggable {
void MyMethod() {
ILoggable loggable = this;
loggable.Log("Using injected logging");
}
}
Run Code Online (Sandbox Code Playgroud)
在 CLR 中,所有接口成员实现都是显式的,因此在您的代码Log中将ILoggable仅在实例中可用,就像在这里推荐的那样:
((ILoggable)this).Log("Using injected logging")
Run Code Online (Sandbox Code Playgroud)
如果您想避免混乱和重复转换,您可以添加一个将类型转换为接口的属性:
public class MyClass : ILoggable
{
ILoggable AsILoggable => (ILoggable)this;
void MyMethod()
{
AsILoggable.Log("Using injected logging");
}
}
Run Code Online (Sandbox Code Playgroud)
但这是关闭的。这似乎是错误的,不管它是如何完成的。从文档:
最常见的场景是安全地将成员添加到已被无数客户端发布和使用的接口中。
当有人担心在接口中实现实现时 - 以前没有 - 这就是有意义的句子。这是一种在不破坏已经实现它的类的情况下添加到接口的方法。
但是这个问题意味着我们正在修改类以反映对其实现的接口的更改。它与此语言功能的所述用例完全相反。
如果我们已经在修改类,为什么不直接实现该方法呢?
public void Log(string message) => DoSomethingWith(message);
Run Code Online (Sandbox Code Playgroud)
当我们添加默认接口实现时,我们为接口的使用者提供了一个实现——依赖于抽象的类。
如果我们依赖于实现接口的类内部的默认接口实现,那么对接口的更改实际上变成了对类内部实现的更改。那不是接口的用途。接口代表面向外部的行为,而不是内部实现。
就好像这个类走出了自身,将自身视为外部消费者,并将其用作其内部实现的一部分。类不实现接口,但它依赖于它。这很奇怪。
我不会说这是错误的,但感觉就像是对该功能的滥用。
将类强制转换为接口的答案的问题在于,它可能会也可能不会调用默认接口方法,具体取决于该类是否实现了覆盖默认方法的方法。
所以这段代码:
((ILoggable)this).Log(...)
Run Code Online (Sandbox Code Playgroud)
最终会调用默认接口方法,但前提是类中没有定义覆盖默认方法的接口方法。
如果类中有一个方法覆盖了默认方法,那么该方法就会被调用。这通常是所需的行为。但是,如果您总是想调用默认方法,无论实现类是否已实现该接口方法的自己版本,那么您有几个选择。一种方法是:
请参阅此答案的代码示例,以及调用默认接口方法的替代方法。