简体   繁体   中英

How to use Inheritance Abstract Class?

I need some help with an example of Inheritance class (abstract). I can't use my function ToCSV() because i return a list of BananaPart and C# wants a list of FruitPart. How is it possible to solve this?

using System;
using System.Collections.Generic;
                    
public class Program
{
    public static void Main()
    {
        FruitFactory fruitFactory = new FruitFactory();
        Fruit myFruit = fruitFactory.Create("banana");
        
        myFruit.FruitPart.ForEach(f => Console.WriteLine(f.ToString()));
    }
}

public class FruitFactory {
    public Fruit Create(string fruitType) { 
        switch(fruitType) {
            case "banana":
                return new Banana();
                break;
            default:
                throw new Exception("undefined fruitType");
        }   
    }
}

public abstract class Fruit {
    protected string _name;
    protected List<FruitPart> _fruitPart;
    
    public Fruit() {
        this._name = "A fruit";
    }
    
    public string Name { get { return this._name; } }
    public List<FruitPart> FruitPart { get { return this._fruitPart; } }
}

public abstract class FruitPart { }

public class Banana : Fruit {
    public Banana() : base() {
        this._name = "banana";
        this._fruitPart = ToCSV();
    }
    
    public List<BananaPart> ToCSV(){
        return new List<BananaPart> { new BananaPart(5, "10"), new BananaPart(10, "20"), new BananaPart(20, "40") };
    }
}

public class BananaPart : FruitPart {
    private int _seed;
    private string _dimensionPart;
    
    public BananaPart (
        int seed,
        string dimensionPart
    ) {
        this._seed = seed;
        this._dimensionPart = dimensionPart;
    } 
}

It will be a pleasure for me to learn more about it ! Thank you in advance !

The reason that your code is not compiling is because List<T> is not covariant. In order to compile your code you need to change List<T> with IEnumerable<T> which has a covariant type parameter. So the changes are:

public abstract class Fruit {
    protected string _name;
    protected IEnumerable<FruitPart> _fruitPart;

    public Fruit() {
      this._name = "A fruit";
    }

    public string Name { get { return this._name; } }
    public IEnumerable<FruitPart> FruitPart { get { return this._fruitPart; } }
}

You can learn more about covariance here: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/

In short, covariance lets you deduce the assignment compatibility of two types based on the assignment compatibility of two other types. Since, we can assign a BananaPart to a FruitPart , we can deduce that a IEnumerable<BananaPart> can be assigned to IEnumerable<FruitPart> .

There is a problem in your model. Say I have a Banana, and it has a list of fruit-parts. Now I could call myBananana.FruitParts.Add(new ApplePart()) , this cannot work since a banana is only composed of bananaparts.

To avoid this you need to be more restrictive in what you return. Instead of using a List<FruitPart> you could use IEnumerable<Fruitpart> . This avoids the problem since you cannot add anything to a IEnumerable .

If you are learning I would also recommend starting with interfaces instead of abstract classes. The later is sometimes called "Implementation inheritance", ie it is used to share code between two derived classes. This is sometimes useful, but in the majority of cases it is better to put this shared code in a third class, sometimes called "Composition over inheritance".

Your fruit could ook something like this using interfaces:

public interface IFruit
{
    string Name { get; }
    IEnumerable<IFruitPart> FruitPart { get; }
}
public class Banana : IFruit
{
    public Banana() : base() => FruitPart = ToCSV();
    public static List<BananaPart> ToCSV() => new List<BananaPart> { new BananaPart(5, "10"), new BananaPart(10, "20"), new BananaPart(20, "40") };
    public string Name { get; } = "Banana";
    public IEnumerable<IFruitPart> FruitPart { get; }
}
public interface IFruitPart { }
public class BananaPart : IFruitPart
{
    public int Seed { get; }
    public string DimensionPart { get; }
    public BananaPart(int seed, string dimensionPart) => (Seed, DimensionPart) = (seed, dimensionPart);
}

Notice that you do not need much more code for the interface variant compared to the abstract class case.

You cannot safely cast List<Child> to List<Parent> because it would let you add Apple to List<Banana> .

You can create List<FuitPart> instead.

    public List<FruitPart> ToCSV()
    {
        return new List<FruitPart> { 
            new BananaPart(5, "10"), 
            new BananaPart(10, "20"), 
            new BananaPart(20, "40") };
    }

As List<BananaPart> doesn't inherite List<FruitPart> you can't do it like that.

I'll suggest you to make some wrapper classes, like FruitedDetails and BananaDetails that would inherite it. That way you could encapsulate the specific details of classes you are working with and wouldn't have to deal with various casting of objects when you pack your lists.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM