从实现类调用C#接口默认方法

时间:2019-09-02 19:22:49

标签: 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
    }
}

我收到一个编译器错误:“该名称在当前上下文中不存在”

是否不可能以这种方式使用默认方法实现?

编辑:

有关C#规则的正确响应,请参见accepted answer。有关更简洁的解决方案(我的问题的初衷!),请参阅下面的own answer

7 个答案:

答案 0 :(得分:7)

请参阅https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/default-interface-members-versions上的文档

  

SampleCustomerICustomer的转换是必要的。 SampleCustomer类不需要为ComputeLoyaltyDiscount提供实现;由ICustomer界面提供。但是,SampleCustomer类不会从其接口继承成员。该规则没有改变。为了调用在接口中声明和实现的任何方法,该变量必须是接口的类型,在此示例中为ICustomer

所以方法类似于

public class MyClass : ILoggable {
    void MyMethod() {
        ILoggable loggable = this;
        loggable.Log("Using injected logging");
    }
}

答案 1 :(得分:3)

在CLR中,所有接口成员的实现都是显式的,因此在您的代码ILoggable中仅在((ILoggable)this).Log("Using injected logging") 的实例中可用,就像建议执行here

{
   "token" : token_value
}

答案 2 :(得分:1)

通过阅读有关这些默认方法的文章,我认为您应该尝试将其转换为界面:

((ILoggable)this).Log("Using injected logging")

我还没有检查过,只是根据this文章的想法

答案 3 :(得分:1)

接受的答案和其他答案是正确的。 但是,我想要的是一个Log方法的简洁调用。 我通过ILoggable接口上的扩展方法实现了这一点:

public static class ILoggableUtils { // For extension methods on ILoggable
    public static void Log(this ILoggable instance, string message) {
         DoSomethingWith(message, instance.SomePropertyOfILoggable);
    }
}

通过这种方式,我至少可以在课堂上致电this.Log(...);而不是丑陋的((ILoggable)this).Log(...)

答案 4 :(得分:1)

如果要避免混乱和重复的转换,可以添加一个将类型转换为接口的属性:

public class MyClass : ILoggable 
{
    ILoggable AsILoggable => (ILoggable)this;
    void MyMethod() 
    {
        AsILoggable.Log("Using injected logging"); 
    }
}

但这是关闭。无论如何完成,这似乎都是错误的。从文档中:

  

最常见的情况是将成员安全地添加到已经由无数客户端发布和使用的界面中。

当有人担心在接口中有实现时-以前没有实现-这句话很有意义。这是一种在不破坏已经实现接口的类的情况下添加到接口的方法。

这个问题意味着我们正在修改类,以具有对新方法的“意识”。换句话说,隐含的约束是无论出于何种原因,修改类以解决新的接口方法都是不切实际的。修改类以依赖于新方法表明完全相反。

如果我们已经在修改类,为什么不仅仅实现该方法呢?

public void Log(string message) => DoSomethingWith(message);

当我们添加默认接口实现时,我们为接口的使用者提供了一个实现-依赖于抽象的类。

如果我们依赖内部中实现接口的类的默认接口实现,则对接口的更改实际上将变为对类的内部实现的更改。那不是接口的目的。接口表示外部行为,而不是内部实现。

就好像该类正在逐步超越自身,以外部使用者的身份回顾自身,并将其用作内部实现的一部分。该类没有实现接口,但是依赖于它。太奇怪了。

我不会说这是错误的,但是它感觉就像滥用功能一样。

答案 5 :(得分:0)

我的解决方案是在接口及其实现之间添加新的抽象类:

public interface ILoggable {
    void Log(string message);
    void SomeOtherInterfaceMethod();
}

public abstract class Loggable : ILoggable  {
    void Log(string message) => DoSomethingWith(message);
    public abstract void SomeOtherInterfaceMethod(); // Still not implemented
}

public class MyClass : Loggable {
    void MyMethod() {
        Log("Using injected logging"); // No ERROR
    }

    public override void SomeOtherInterfaceMethod(){ // override modifier needed
        // implementation
    };
}

答案 6 :(得分:0)

将类转换为接口的答案的问题在于,它可能会或可能不会调用默认接口方法,这取决于该类是否实现了覆盖默认方法的方法。

所以这段代码:

((ILoggable)this).Log(...)

最终调用默认接口方法,但前提是类中没有定义覆盖默认方法的接口方法。

如果类中存在覆盖默认方法的方法,则该方法将被调用。这通常是所需的行为。但是,如果你总是想调用默认方法,不管实现类是否已经实现了它自己的接口方法版本,那么你有几个选择。一种方法是:

  1. 将默认方法声明为静态方法。不用担心,您仍然可以在继承自它的类中覆盖它。
  2. 调用类的静态方法时,使用相同类型的语法调用默认方法,只用接口名替换类名。

有关代码示例以及调用默认接口方法的替代方法,请参见 this answer