2

I have a base class which has a method call AddFruit that takes a class of Fruit type and processes it in a general way.

    public abstract class Foo
    {
        protected List<ProcessedFruit> processedFruit = new List<ProcessedFruit>();

        public void AddFruit(Fruit o)       
        {
            // Process fruit

            processedFruit.Add(o);
        }

        public void Update()
        {
            // Do base class specific stuff here
            OnUpdate();
        }

        protected abstract void OnUpdate();
    }

    public class AppleBar : Foo
    {
        public AppleBar()
            :base(){}

        protected override void OnUpdate() { }
    }

    public class BananaBar : Foo
    {
        public BananaBar()
            :base(){}

        protected override void OnUpdate() { }
    }

Any class which derives from Foo is updated in a non general way and will use the list of ProcessedFruit in a different manner.

Fruit can be added and processed any time after the Bar class has been instantiated.

    public abstract class Fruit
    {

    }

    public class Banana : Fruit
    {

    }

    public class Apple : Fruit
    {

    }

I'm wondering, is it possible to only allow a particular type of Fruit class to be added based on the derived Bar class type?

For example:

  • AppleBar will only allow the adding of type Apple
  • BananaBar will only allow the adding of type Banana

I understand that I could override the AddFruit method but I would like the processing to remain in the base class and would like to avoid calling base.AddFruit in the overridden methods associated with the BananaBar and AppleBar derived classes.

I also wish to avoid checking the type of Fruit using GetType().

Ideally I would like something as follows:

var o = new AppleBar()

// This has to be an Apple and intellisense can recognise this 
o.AddFruit(...);          

Is this possible?

Edit:

I am having problems with the following using generics:

 List<Foo<Fruit>> commands = new List<Foo<Fruit>>(10);

 commands.Add(new AppleBar());    // Can't be added
 commands.Add(new BananaBar());   // Can't be added
svick
  • 236,525
  • 50
  • 385
  • 514
user1423893
  • 766
  • 4
  • 15
  • 26
  • Out of curiosity, why is Fruit a subclass of ProcessedFruit? – Millie Smith Jun 13 '14 at 20:26
  • Because my naming convention is pretty terrible. I was trying to keep it straightforward and not use my class names. – user1423893 Jun 13 '14 at 20:39
  • If you have a valid use for holding onto a collection of the different `commands`, you could create an interface for your shared behavior, and the List would be of the interface type (such as `List`). – Caleb Jun 13 '14 at 23:11

1 Answers1

6

The easiest way to do this is with a generic type parameter on the base class, which then gets filled in with a specific type by the inheriting class:

public abstract class Foo<T> where T : Fruit
{
    protected List<ProcessedFruit> processedFruit = new List<ProcessedFruit>();

    public void AddFruit(T o)       
    {
        // Process fruit

        processedFruit.Add(o);
    }

    public void Update()
    {
        // Do base class specific stuff here
        OnUpdate();
    }

    protected abstract void OnUpdate();
}

public class AppleBar : Foo<Apple>
{
    //...
}

Update

See this answer for an explanation of why you can't add an AppleBar to a List<Foo<Fruit>>

Community
  • 1
  • 1
Ben Aaronson
  • 6,955
  • 2
  • 23
  • 38
  • If I wanted to keep a `List` of the Foo classes. How would I then add the derived classes to that list? For example: `var fooList = new List();` `fooList.Add(new AppleBar());` The `Add` method does not work in this case? – user1423893 Jun 13 '14 at 20:38
  • Can you create a new List> where T : Fruit? (I don't know, and I'm on a Mac right now). – Millie Smith Jun 13 '14 at 20:45
  • @user1423893 Yeah that won't work because `fooList` is a list of `Foo`s, not a list of `Foo`s. You can't have a list like that which maintains type-safety. If you could add an `AppleBar` to a `List>`, then you could do `Foo appleBar = fooList.First()`. Then because you have a `Foo` rather than a `Foo`, you'd expect to be able to do `appleBar.AddFruit(new Banana())` – Ben Aaronson Jun 13 '14 at 20:48
  • I think I need to rethink my class structure after reading that answer. Thank you for your help and answer. – user1423893 Jun 13 '14 at 20:54
  • 1
    @user1423893 Read up on generic covariance and contravariance, but they only help in certain situations and this doesn't look like one of them. But yes, trying to keep classes of different types in a single list is usually a design smell – Ben Aaronson Jun 13 '14 at 20:56
  • In this case, it certainly is :) – user1423893 Jun 13 '14 at 20:58