0

Let's say i have an abstract class ObjectA. ObjectC and ObjectB inherit from ObjectA. There are a lot more that inherit from ObjectA.

Then, I have methods that do something according to the type of the object.

public void DoSomething(ObjectC data){}
public void DoSomething(ObjectB data){}

I was wondering if there's a way to call the method that i need according to the type of the object, that's because i don't know what kind of Object i will get.

public void DoSomething(ObjectA data){
    DoSomething(data);
}

Currently i am using

public void DoSomething(ObjectA data){
    if (data is ObjectB dataB){
        DoSomething(dataB);
    }else if (data is ObjectC dataC){
        DoSomething(dataC);
    }
}

I was wondering if there's a way to avoid the usage of a if/else.

Thanks.

Asdren
  • 89
  • 4

2 Answers2

3

The traditional way to do this is to invert what you are asking and to use virtual functions to implement polymorphism in the fashion described by @RuiJarimba in the comments. For example, consider these three classes (I renamed your ObjectX classes to ClassX):

public abstract class ClassA
{
    public abstract void DoSomething();
}

public class ClassB : ClassA
{
    public override void DoSomething()
    {
        //Do something that is ClassB specific
    }
}

public class ClassC : ClassA
{
    public override void DoSomething()
    {
        //Do something that is ClassC specific
    }
}

Every instance of ClassB and ClassC is inherently a ClassA (through inheritance). Both ClassB and ClassC implement a DoSomething method that overrides the abstract definition that ClassA has defined. Now, I can take any ClassA object (which has to be an instance of a sub-class of ClassA since ClassA is abstract) and call DoSomething() on it and get a class-specific implementation of DoSomething(). In the code below, I create separate instances. In real life, you normally have a collection of ClassA object references that you walk through calling appropriate methods:

   ClassA objectB = new ClassB();
   ClassA objectC = new ClassC();

   objectB.DoSomething();      //calls ClassB's version
   objectC.DoSomething();      //calls ClassC's version

It's not exactly what you are asking about, but I think it's what you want.

Continuing On

If you want to keep that same function signature, you can add the following to the ClassA definition:

  public static void DoSomething<T>(T data) where T : ClassA
  {
      data.DoSomething();     //dispath through the right class using a virtual function
  }

If you do that, then this will work (using the objectB and objectC instances created above:

    ClassA.DoSomething(objectB);      //calls ClassB's version
    ClassA.DoSomething(objectC);      //calls ClassC's version

What the new code does is take the object it is passed (which is necessarily an instance of a ClassA subclass) and simply calls the previously defined virtual function, dispatching things to the appropriate class's implementation.

Flydog57
  • 6,851
  • 2
  • 17
  • 18
  • This isn't a bad solution either but who wants to keep creating new objects to call eeach method? – Yusha Sep 18 '18 at 20:19
  • Sorry, I'm not following. The traditional example of this is `abstract class Shape` with concrete classes like `Rectangle`, `Circle`, etc. with a method named `Draw`. You write a program that allows instantiation of various kind of shape objects, and your draw routine just walks the list and lets each shape draw itself. If you look at the OP's question, he has two objects (one an `ObjectB`, the other an `ObjectC`), each called `data`. He does something to each one. If you don't have multiple objects of different types, the question is moot. – Flydog57 Sep 18 '18 at 20:26
1

You can use the is operator and casting. Check out:

https://dotnetfiddle.net/uvjHHV

using System;

public class Program
{

    public abstract class ObjectA { }

    public class ObjectB : ObjectA {}

    public class ObjectC : ObjectA {}

    public static void Main()
    {
        // You may get this instance from anywhere
        Object o = new ObjectB();

        if (o is ObjectB) 
            DoSomething((ObjectB) o);
        else if (o is ObjectC)
            DoSomething((ObjectC) o);

    }


    public static void DoSomething(ObjectB o) {
        Console.WriteLine("Object B called");   
    }

    public static void DoSomething(ObjectC o) {
        Console.WriteLine("Object C called");   
    }
}
João Menighin
  • 3,083
  • 6
  • 38
  • 80
  • Did you intend for `o` to be declared as `Object` or did you mean for it to be `ObjectA`? It works in either case, but I think `ObjectA` is better. – Rick Davin Sep 18 '18 at 17:32
  • In the fiddle is `ObjectA`, I changed to `Object` just to show that it can be literally any object. – João Menighin Sep 18 '18 at 17:41
  • This is one way to do it, but the if / else is unfortunate. – Yusha Sep 18 '18 at 17:42
  • If `DoSomething` is not a member function from `ObjectA` there is no other way of doing it... You have to check what type you are receiving with `if/else` somewhere... – João Menighin Sep 18 '18 at 17:44
  • That's the way it is implemented right now, the problem is, there are a lot of types so there are a lot of `if/else`. – Asdren Sep 18 '18 at 17:56
  • @Asdren, again: if `DoSomething` is not a member function, as @Flydog57 described on his answer, there isn't other way to do it that you wouldn't have to check for the types with `if/else` somewhere. If you make `DoSomething` part of the `ObjectA` class, then you can use override and polymorphism to achieve it. Just like the answer from Flydog. – João Menighin Sep 18 '18 at 19:03
  • Actually, maybe you could use some really ugly reflection to iterate through your functions and calling the right one. But this is so not recommended I wont even write an example... n_n' – João Menighin Sep 18 '18 at 19:04
  • If you are going to do this in this fashion, you can use the new form of `switch`. Find the latest docs on `switch` and search for "Pattern Matching" on types: https://learn.microsoft.com/en-us/dotnet/csharp/pattern-matching. It reads a lot better than a cascading series of `else if` statements. It's also less prone to mis-typed errors – Flydog57 Sep 18 '18 at 20:14
  • You **don't** have to use multiple if / else... @Asdren did you check out my solution? – Yusha Sep 18 '18 at 20:18
  • @Yusha, I don't see how your answer solves the problem... Its not about having various `DoSomething`, it's about knowing which one to call... Your answer does not address this... – João Menighin Sep 18 '18 at 20:20