How to get actual type of an derived class from its parent interface

50,723

Solution 1

GetType gets you the exact runtime type of an object. From the documentation:

The Type instance that represents the exact runtime type of the current instance.

You can also use is to determine if an object is an instance of a specific type:

var noise = (obj is Velociraptor) ? "SKREEE!" : "<unknown>";

Why do you need the exact runtime type, though? The entire point of an interface is that you should be hiding the implementation details behind the common interface. If you need to take an action based on the type, that's a big hint that you're violating the encapsulation it provides.

One alternative is to use polymorphism:

public interface IVocalizer { string Talk(); }

public class Doorbell : IVocalizer {
  public string Talk() { return "Ding-dong!" }
}
public class Pokemon : IVocalizer {
  public string Talk() {
    var name = this.GetType().ToString();
    return (name + ", " + name + "!").ToUpper(); } // e.g., "PIKACHU, PIKACHU!"
}
public class Human : IVocalizer {
  public string Talk() { return "Hello!"; }
}

Since these three types aren't related at all, inheritance from a common type doesn't make sense. But to represent that they share the same capability of making noise, we can use the IVocalizer interface, and then ask each one to make a noise. This is a much cleaner approach: now you don't need to care what type the object is when you want to ask it to make a noise:

IVocalizer talker = new ???();  // Anything that's an IVocalizer can go here.

// elsewhere:
Console.WriteLine(talker.Talk());    // <-- Now it doesn't matter what the actual type is!
                                     //   This will work with any IVocalizer and you don't
                                     //   need to know the details.

Solution 2

Object.GetType returns the exact runtime type of the instance. That is what you should use.

Although, generally speaking, you shouldn't care at all what the runtime type of an interface is - if you're writing code to determine that, it probably reflects an error in your design somewhere.

In fact, the interface name IProduct is already a bit of a code smell. It's not wrong, per se, but interfaces are meant to define the actions available on a particular object, i.e. what it does. The name IProduct seems to be describing what it is, not what it does, which is more appropriate for an abstract base class. This isn't a "rule", mind you, but it's a good guideline to follow.

When writing methods/classes that depend on an abstract type (base class or interface), if you find that you're depending on the more derived types or specific implementations, it means that either your abstract type is anemic (does not have enough functionality to be used effectively), or your dependency has too much coupling (depends on implementation details instead of abstractions).

Consider expanding the Product/IProduct to do more, or make your dependency actually work on specific product types through method overloading.

Solution 3

if (product is Book)
{
   ...
}
else if (product is Soda)
{
   ...
}

Solution 4

While GetType() will return the actual type, you should use the is operator.

Typically, you shouldn't need to do this. Normally, what you do is simply defer the behavior to the child class and invoke it via the interface. For example, if you have different requirements for Soda vs. Book (say, Soda requires collecting tax, whereas Book doesn't), then you'd create a Sell method on the interface and then in your SellThisProduct() method, you'd simply call the Sell() method on the object.

public interface IProduct
{
   public decimal Sell(); // computes price
   ...
}

.....

IProduct product = ProductCreator.CreateProduct(); //Factory Method we have here
SellThisProduct(product);

//...

private void SellThisProduct(IProduct product)
{
   var price = product.Sell();
   ...
}

internal class Soda : IProduct
{
   public decimal Sell()
   {
       this.Price + TaxService.ComputeTax( this.Price );
   }
}

internal class Book : IProduct
{
    public decimal Sell()
    {
         return this.Price;
    }
}

Solution 5

typeof(product) will return IProduct.

product.GetType() will actually return the derived type of the object since it's a member function.

Share:
50,723
Tarik
Author by

Tarik

I am a software engineer with interests in different technologies.

Updated on July 09, 2022

Comments

  • Tarik
    Tarik almost 2 years

    Let's say we have a code portion like this:

    IProduct product = ProductCreator.CreateProduct(); //Factory method we have here
    SellThisProduct(product);
    
    //...
    
    private void SellThisProduct(IProduct product)
    {
      //.. Do something here
    }
    
    //...
    
    internal class Soda : IProduct
    {}
    
    internal class Book : IProduct
    {}
    

    How can I infer which product is actually passed into SellThisProduct() method in the method?

    I think if I say GetType() or something it will probably return the IProduct type.

  • Tarik
    Tarik about 14 years
    By the way Pokemon you say :D
  • BigChrisDiD
    BigChrisDiD about 14 years
    I've just realised how much bad polymorphism I've committed in my app. BAD programmer! I got a hell of a lot of case statement to re-jig.
  • John Feminella
    John Feminella about 14 years
    Although it's true that GetType() will return the derived type, it doesn't have anything to do with being a member function, per se. (You could easily write a static method called TypeGet() which would do the same thing.)
  • EMP
    EMP about 14 years
    typeof(product) will not compile - the typeof operator expects a type, not an instance. typeof(IProduct) will, of course, return IProduct.
  • Eddie
    Eddie almost 11 years
    Just FYI, if you're dealing with a WCF proxy, then GetType() does not return the exact runtime type of the instance. It returns an interface as the type of the actual, concrete object you have. I think this has something to do with a transparent proxy object.
  • Charles Bretana
    Charles Bretana about 9 years
    Except if it is a nullable type, then GetType returns the Underlying Type of the object, not the Nullable Type