0

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?

CXO2
  • 628
  • 10
  • 28
  • Just a sidenote: The declaration of method `Interact()` inside class `Entity`. It must be `public abstract`. – Flat Eric Mar 18 '16 at 15:03
  • You can check type inside `Interact()` method implementation and throw `ArgumentException` if the type is not what you're expecting. – Alex Mar 18 '16 at 15:04
  • @FlatEric thanks for the correction! I updated the question – CXO2 Mar 18 '16 at 16:06

1 Answers1

2

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..
    }
}
MikeT
  • 5,398
  • 3
  • 27
  • 43
  • Thanks for the answer and explanation! The generic ones is achieve something that i want. However, is it possible to add more "constraint" on it? for example, I have more two classes called `FooBar : Entity>, IFoo, IBar` and `BarFoo : IFoo, IBar`, then I want `FooBar` class is able to `Interact` with another class that implement `IFoo` and `IBar`, In other words, it can interact with another `FooBar` instance, `BarFoo` instance and any class that implement `IFoo` and `IBar` interfaces. Is it possible? what generic parameter should I pass to `Entity<>`? – CXO2 Mar 18 '16 at 16:12
  • if you specify where T:IFoo then T can be any class that implements the foo interface. however in that case you would probably want to move interact into the Foo interface, and then create a simalar method in the bar interface that only takes IBar input, if that wont work then you'll have to accept that your code can't be checked at compilation and needs runtime checks as the other answers suggest – MikeT Mar 18 '16 at 16:39
  • you could also create an IEntity interface that Entity inherits as well as IBar and IFoo then you could set where T:IEntity, which would allow IFoo and I bar as they both inherit from IEntity but not Entity – MikeT Mar 18 '16 at 16:45