Home > OS >  Why does this code not return the class instance from the interface?
Why does this code not return the class instance from the interface?

Time:07-08

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 {
    public IInterface DoSomething() => ((IInterface)this).DoSomething(); // Infinite recursion
    //public Class DoSomething() => (Class)((IInterface)this).DoSomething(); // Works
}

CodePudding user response:

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:

CodePudding user response:

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
  • Related