Calling C# interface default method from implementing class

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-10 04:24:56

问题


C# 8 supports default method implementations in interfaces. My idea was to inject a logging method into classes like this:

public interface ILoggable {
    void Log(string message) => DoSomethingWith(message);
}

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

I get a compiler error: "The name does not exist in the current context"

Is it impossible to use default method implementations in this way?

EDIT:

For the correct response regarding C# rules, see the accepted answer. For a more concise solution (the original idea of my question!) see my own answer below.


回答1:


See the documentation at https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/default-interface-members-versions

That cast from SampleCustomer to ICustomer is necessary. The SampleCustomer class doesn't need to provide an implementation for ComputeLoyaltyDiscount; that's provided by the ICustomer interface. However, the SampleCustomer class doesn't inherit members from its interfaces. That rule hasn't changed. In order to call any method declared and implemented in the interface, the variable must be the type of the interface, ICustomer in this example.

So the method is something like

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



回答2:


In CLR all interface member implementations are explicit, so in your code Log will be available in instances of ILoggable only, like it's recommended to do here:

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



回答3:


From reading an article about these default methods I think you should try to upcast it to the interface:

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

I haven't checked it, just my thought according to this article




回答4:


The accepted answer and the other responses are correct. However, what I wanted is a concise call of the Log method. I achieved that with an extension method on the ILoggable interface:

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

In this way, I can at least call this.Log(...); in my class instead of the ugly ((ILoggable)this).Log(...).




回答5:


If you want to avoid clutter and repetitive casting you can add a single property which casts the type as the interface:

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

But this is off. It seems wrong, regardless of how it's done. From the documentation:

The most common scenario is to safely add members to an interface already released and used by innumerable clients.

When there was some concern about having implementations in interfaces - which previously had none - this was the sentence that made sense of it. It's a way to add to an interface without breaking classes that already implement it.

This question implies that we are modifying the class to have an "awareness" of the new method. In other words, the implied constraint is that for whatever reason it's impractical to modify the class to account for the new interface method. Modifying the class to depend on the new method indicates exactly the opposite.

If we're already modifying the class, why not just implement the method?

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

When we add a default interface implementation, we provide an implementation to consumers of the interface - classes that depend on an abstraction.

If we depend on the default interface implementation from within the class that implements the interface, then a change to the interface becomes, in effect, a change to the internal implementation of the class. That's not what an interface is for. An interface represents external-facing behavior, not internal implementation.

It's as if the class is stepping outside of itself, looking back in at itself as an external consumer, and using that as part of its internal implementation. The class doesn't implement the interface, but it depends on it. That's weird.

I won't go so far as to say that it's wrong, but it feels like an abuse of the feature.



来源:https://stackoverflow.com/questions/57761799/calling-c-sharp-interface-default-method-from-implementing-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!