-1

When this code is run (I'm using .NET 6.0), it recurses infinitely, and never gets to DoSomething() in IInterface, instead of returning the Class instance from the interface.

It seems because of the return type of the method in the class being the same as in the interface, the compiler seems to think the interface's method is being reimplemented in the class, and the method calls itself.

If the method's return type is changed to the concrete class, it works without a problem. Why is it?

using System;
                    
public class Program
{
    public static void Main()
    {
        var obj = new Class();
        var ret = obj.DoSomething();
        Console.WriteLine("Finished");
    }
}

interface IInterface {
    IInterface DoSomething() {
        return new Class();
    }
}

class Class : IInterface {
    // Infinite recursion
    public IInterface DoSomething() => ((IInterface)this).DoSomething(); 

    // Works
    //public Class DoSomething() => (Class)((IInterface)this).DoSomething(); 
}
Matthew Andrews
  • 437
  • 1
  • 5
  • 16
user182917
  • 1,688
  • 2
  • 14
  • 17
  • @Charlieface, this didn't seem to work, the infinite recursion still occurred. – user182917 Jul 08 '22 at 01:00
  • 1
    Hmmm, relevant https://stackoverflow.com/questions/58112748/c-sharp-8-0-default-interface-implementations-base-syntax-explicit-invocations and others. Seems you should be able to do `base(IInterface).DoSomething()` but not sure if that feature is released yet – Charlieface Jul 08 '22 at 01:27

2 Answers2

2

If you mark your interface method as sealed it will prevent recursion, but you won't be able to re-implement the method in another class

interface IInterface {
    sealed IInterface DoSomething() {
        return new Class();
    }
}

class Class : IInterface {
    public IInterface DoSomething() => ((IInterface)this).DoSomething();
}

Alternatively, you could make the class implementation private, and access it from another method. This will allow you to re-implement the interface in another class.

internal class Class : IInterface
{
    private IInterface DoSomething() => ((IInterface)this).DoSomething();
    public IInterface DoSomethingPublic() => DoSomething();
}

Unfortunately, there just isn't support for what you want to do currently. There was a section in the original Default Interface Methods proposal about the possibility of using base() to explicitly call an inherited interface, but that was cut.

I admit that I am not very familiar with default interface methods, but I suspect that the default behavior is to override the implementation, and by marking the interface as sealed you prevent this from happening.

If someone has a better explanation, please correct me!

Some reading material:

Matthew Andrews
  • 437
  • 1
  • 5
  • 16
  • Right, it worked, but then it's not possible to implement the method if I want to. I thought the fact that the interface is clearly called in the body of the method in the class would have equal bearing on the decision by the compiler on whether it's a reimplementation, but it seems only the method signature (and sealed attribute on the interface) seem to be taken into account. – user182917 Jul 08 '22 at 02:04
  • @PedroOS I'm not sure I understand the problem you're facing. Do you mind giving me a more detailed example of why this implementation is not sufficient? – Matthew Andrews Jul 08 '22 at 02:14
  • Sure. In this example: https://dotnetfiddle.net/x20FpG I use 'sealed' then I'm forbidden from re-implementing the interface method in another class. I didn't want to close my interface for re-implementation if I can simply switch the return type in the implementing class to a concrete type to avoid the recursion and use the interface's default implementation. – user182917 Jul 08 '22 at 02:47
  • 1
    @PedroOS I see now how my solution isn't ideal. Unfortunately, you don't really have many more options with the language currently. You're probably better off switching back to a concrete type. Another consideration is making the class implementation private and accessing it from another method, this would prevent the recursion. I will update my answer with a brief example just in case it could help someone else. – Matthew Andrews Jul 08 '22 at 04:13
0

A. Base class

Classic base class implementation:

class BaseClass {
    protected void DoSomething() {
        Console.WriteLine("Hello from BaseClass!");
    }
}

class Class : BaseClass {
    public new void DoSomething() // new or virtual + override
    { 
        Console.WriteLine("Hello from Class"); 
        base.DoSomething();
    } 
}

B. Helper method

static class Helper {
   public static void DoSomething() => Console.WriteLine("Do something!");    
}


interface IInterface {
    void DoSomething() => Helper.DoSomething();
}

class Class : IInterface {
    public void DoSomething() { Console.WriteLine("Hello from Class"); Helper.DoSomething();} 
}

C. Static interface method

I would say that this seems to be a case for a base class not an interface, but one way to 'reuse' the interface method is to make the interface method static.

For example:

var obj = new Class();
obj.DoSomething();
Console.WriteLine("Finished");
    
interface IInterface {
    static void DoSomething() {
        Console.WriteLine("Hello from IInterface!");
    }
}

class Class : IInterface {
    public void DoSomething() { Console.WriteLine("Hello from Class"); IInterface.DoSomething();} 
}

This prints:

Hello from Class
Hello from IInterface!
Finished
tymtam
  • 31,798
  • 8
  • 86
  • 126