Default implementation in interface is not seen by the compiler?
Asked Answered
M

2

23

Here is a my code inside a c# project that targets .NET Core 3.0 (so I should be in C# 8.0) with Visual Studio 2019 (16.3.9)

public interface IJsonAble
{
    public string ToJson() => System.Text.Json.JsonSerializer.Serialize(this);
}

public class SumRequest : IJsonAble
{
    public int X { get; set; }

    public int Y { get; set; }

    public void Tmp()
    {
        new SumRequest().ToJson(); //compile error
    }
}

The compile error is:

CS1061 'SumRequest' does not contain a definition for 'ToJson' and no accessible extension method 'ToJson' accepting a first argument of type 'SumRequest' could be found (are you missing a using directive or an assembly reference?)

Can someone shed some light on this behavior ?

Mob answered 20/11, 2019 at 9:26 Comment(5)
Even with C# 8.0, the class doesn't inherit the implementation from the interface. You have to cast to the interface type to use the default implementation.Jalousie
Although C# 8 allows concrete method implementation in interfaces (https://mcmap.net/q/298880/-default-implementation-of-a-method-for-c-interfaces), I still don't think is is a good idea OOP-wise, I suggest you change interface IJsonAble to abtract class AbstractJsonAble. I know this comment isn't directly related to your problem it may be helpful to keep in mind.Phenformin
Ran into the same issue, this is mental! I mean the entire purpose of default impl. on interfaces is that we no longer have to add common functionality as extensions on our own interfaces. If this should be even close to making any sense then neither regular interface methods nor extensions for interfaces should be seen through a reference to an implementing type. I would really love to see the reasoning behind this decision.Gunter
@Gunter reason is "it allows you to add to the interface without worrying about the downstream consequences" (see accepted answer)Mob
@Gunter Strongly agreed. The term "default" implies that it may be optionally overridden by the implementing class. To find out that you must first cast it as an interface type boggles my mind. This could have been a simple and immensely useful feature.Largent
E
27

Methods are only available on the interface, not the class. So you can do this instead:

IJsonAble request = new SumRequest()
var result = request.ToJson();

Or:

((IJsonAble)new SumRequest()).ToJson();

The reason for this is it allows you to add to the interface without worrying about the downstream consequences. For example, the ToJson method may already exist in the SumRequest class, which would you expect to be called?

Entoil answered 20/11, 2019 at 9:30 Comment(4)
> "...which would you expect to be called?" The class-implemented one. If the class doesn't implement one, then the default method defined in the interface should be called. It's not hard to reason through. This news is sorely disappointing.Largent
@Largent What if there are two interfaces implemented that later add ToJson? There are a lot of weird cases that can eventuality come up, I'm guessing the reasoning is similar to why C# does not allow multiple inheritance. At least c++ made it cleaner to specify which parent method you're interested in.Arrant
@FrankSchwieterman > "What if there are two interfaces implemented that later add ToJson?" Then they both get called in sequence based on the order they're listed in the class declaration.Largent
Calling both in sequence is a disaster. But in those cases the compiler could have forced the inheriting class to specify which one it is calling (or implement its own). This feature is useless in its current stateHoehne
S
2

a bit of a necro but if you want to implicitly call the default method implementation without HAVING to implement it manually, you can make an extension method for the Interface itself:

public static class JsonAbleExtensions {
    public static string ToJson(this IJsonAble jsonAble)
    {
        return jsonAble.ToJson();
    }
}

This avoids using explicit runtime casting or forcing users of the interface to implement the method manually on all classes that inherit from it.

Spacing answered 20/11, 2023 at 19:33 Comment(1)
This is fine, but it doesn't answer the question, it is just a workaround.Entoil

© 2022 - 2024 — McMap. All rights reserved.