Search code examples
c#classgenericsinterfaceinterface-implementation

Give compile time error if two classes doesn't implement same interface


Let's say, I have an abstract class 2 interfaces:

public abstract class Entity
{
    public abstract void Interact(Entity entity);
}

public interface IFoo 
{
    void DoFoo();
}

public interface IBar
{
    void DoBar();
}

And now, let's say I have two classes that implement these interfaces:

public class Foo : Entity, IFoo
{
    public override void Interact(Entity entity)
    {
        // do something with entity...
    }

    public void DoFoo()
    {
       // Do foo stuff here..
    }
}

public class Bar : Entity, IBar
{
    public override void Interact(Entity entity)
    {
        // do something with obj..
    }

    public void DoBar()
    {
       // Do bar stuff here..
    }
}

Now the question, since those classes implement same abstract class (Entity), it is possible for Bar to interact with Foo or vice versa, something like this:

var foo = new Foo();
var bar = new Bar();

foo.Interact(bar); // OK!
bar.Interact(foo); // OK too!

But now, I want Foo is only able to interact with another instance of IFoo and give compile time error if it tries to interact with instance of Bar, the same rule should be applied to Bar too. So it should be something like..

var foo = new Foo();
var anotherFoo = new Foo();
var bar = new Bar();

foo.Interact(anotherFoo); // OK!
foo.Interact(bar); // give compile time error
bar.Interact(foo); // this one should give compile time error too

Is it possible to do such thing? If so, how can I do that?


Solution

  • you are confusing a few elements here

    Entity has no relationship with IFoo or IBar
    Foo has a relationship with Entity and IFoo Bat has a relationship with Entity and IBar

    so if you only want to interact with IFoo then you need to specify IFoo as the parent not entity

    public class Foo : Entity, IFoo
    {
        public void Interact(IFoo entity)
        {
            // do something with entity...
        }
    
        public void DoFoo()
        {
           // Do foo stuff here..
        }
    }
    
    public class Bar : Entity, IBar
    {
        public void Interact(IBar entity)
        {
            // do something with obj..
        }
    
        public void DoBar()
        {
           // Do bar stuff here..
        }
    }
    

    as the behaviour of interact isn't shared by all its children then the interact doesn't belong in the parent

    you could get round this with generics though

    public abstract class Entity<T>
     where T:Entity
    {
        void Interact(T entity);
    }
    

    this would then allow you to declare foo as

    public class Foo : Entity<Foo>, IFoo
    {
        public override void Interact(Foo entity)
        {
            // do something with entity...
        }
    
        public void DoFoo()
        {
           // Do foo stuff here..
        }
    }