如何使用继承抽象类?

3

我需要一些关于继承类(抽象)的示例帮助。我无法使用我的ToCSV()函数,因为它返回了BananaPart列表,而C#想要FruitPart列表。有什么解决办法吗?

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;
    } 
}

我很乐意了解更多相关内容!非常感谢您提前的帮助!


1
这个回答解决了你的问题吗?C#支持返回类型协变吗? - Charlieface
4个回答

3
你的代码无法编译的原因是因为List<T>不是协变的。为了让代码编译,你需要将List<T>改为具有协变类型参数的IEnumerable<T>。所需更改如下:
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; } }
}

您可以在此处了解有关协变的更多信息:https://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/covariance-contravariance/

简单来说,协变允许您根据两种其他类型的分配兼容性推断出两个类型的分配兼容性。因为我们可以将 BananaPart 分配给 FruitPart,所以我们可以推断一个 IEnumerable<BananaPart> 可以分配给 IEnumerable<FruitPart>


1
您的模型存在问题。比如我有一个香蕉,它有一些水果部件的列表。现在我可能会调用myBananana.FruitParts.Add(new ApplePart()),但这是不行的,因为香蕉只由香蕉部件组成。
为了避免这种情况,您需要更加严格地限制返回类型。可以使用IEnumerable<Fruitpart>代替List<FruitPart>,这样就可以避免这个问题,因为您无法向IEnumerable添加任何内容。
如果您正在学习,我还建议从接口开始,而不是抽象类。后者有时被称为“实现继承”,即用于在两个派生类之间共享代码。这有时很有用,但在大多数情况下,最好将此共享代码放在第三个类中,有时称为“组合优于继承”。
使用接口,您的水果可能看起来像这样:
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);
}

请注意,与抽象类的情况相比,界面变量不需要更多的代码。

1
你不能安全地将List<Child>转换为List<Parent>,因为这会让你将Apple添加到List<Banana>中。
你可以创建List<FuitPart>代替。
    public List<FruitPart> ToCSV()
    {
        return new List<FruitPart> { 
            new BananaPart(5, "10"), 
            new BananaPart(10, "20"), 
            new BananaPart(20, "40") };
    }

0

由于List<BananaPart>没有继承List<FruitPart>,所以你不能那样做。

我建议你创建一些包装类,比如FruitedDetailsBananaDetails来继承它。这样你就可以封装你正在处理的类的具体细节,并且在打包列表时不必处理各种对象的强制类型转换问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接