Is it wrong to use Type as a "property"?

I am currently working with some old C # code that mostly uses derived types for the sole purpose of using the type as a property, e.g .:

public abstract class Fruit
{
    public int Property { get; set; }
}

public class Apple : Fruit {}

public class Pear : Fruit {}

      

And then:

public void Foo(Fruit item)
{
    if(item is Apple)
    {
        // do something
        return;
    }
    if(item is Pear)
    {
        // do something
        return;
    }

    throw new ArgumentOutOfRangeException("item");
}

      

I would include an enum property on BaseClass to indicate the "type":

public class Fruit
{
    public int Property { get; set; }

    public FruitType Type { get; set; }
}

public enum FruitType
{
    Apple,
    Pear
}

      

and then used it this way:

public void Foo(Fruit item)
{
    switch(item.Type)
    {
        case FruitType.Apple:
            // do something
            break;
        case FruitType.Pear:
            // do something
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
}

      

I feel like the old pattern is a misuse of inheritance, but is there any benefit to it that I should consider before rewriting this code?

+3


source to share


1 answer


The standard "OO" way to handle this situation is to make DoSomething an abstract method on Fruit. The caller then just calls DoSomething, knowing the implementation will be correct.

The downside to this approach is that it is responsible for designing all the possible "things" that the user might want on the author of the abstract class.

This flaw can be mitigated by using a "visitor pattern". The visitor pattern is a standard way for third parties to efficiently switch behavior based on runtime type. You can think about it.

Your second approach - type recognition by tag - is fairly common and can be very effective. Roslyn uses this technique extensively. This is considered by OO purists to be a little smelly, but luckily I'm not an OO purist.



A variation on your second method that I like is:

public enum FruitKind { Apple, Orange }
public abstract class Fruit
{
  private Fruit(FruitKind kind)
  {
      this.Kind = kind;
  }
  public FruitKind Kind { get; protected set; }
  private class Apple : Fruit
  {
      public Apple() : base(FruitKind.Apple) {}
  }
  public static Fruit MakeApple() { return new Apple(); }
  // similarly for orange
}

      

Now the only way the Fruit user can tell the type is with a tag, because Apple and Orange are not available. You know that no third party is going to create their own fruit, because the only Fruit constructor is private.

+12


source







All Articles