【问题标题】:How to use Inheritance Abstract Class?如何使用继承抽象类?
【发布时间】:2021-07-25 22:51:44
【问题描述】:

我需要一些关于继承类(抽象)示例的帮助。我不能使用我的函数 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;
    } 
}

我很高兴能更多地了解它!提前谢谢你!

【问题讨论】:

标签: c# class inheritance abstract-class


【解决方案1】:

您的代码未编译的原因是List&lt;T&gt; 不是协变的。为了编译您的代码,您需要将List&lt;T&gt; 更改为具有协变类型参数的IEnumerable&lt;T&gt;。所以变化是:

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://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/

简而言之,协方差可以让您根据其他两种类型的赋值兼容性来推断两种类型的赋值兼容性。因为,我们可以将BananaPart 分配给FruitPart,我们可以推断IEnumerable&lt;BananaPart&gt; 可以分配给IEnumerable&lt;FruitPart&gt;

【讨论】:

    【解决方案2】:

    您的模型有问题。假设我有一个香蕉,它有一个水果部分的列表。现在我可以打电话给myBananana.FruitParts.Add(new ApplePart()),这是行不通的,因为香蕉只由香蕉部分组成。

    为避免这种情况,您需要对返回的内容进行更多限制。您可以使用IEnumerable&lt;Fruitpart&gt;,而不是使用List&lt;FruitPart&gt;。这样可以避免问题,因为您无法向 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);
    }
    

    请注意,与抽象类案例相比,接口变体不需要更多代码。

    【讨论】:

      【解决方案3】:

      您不能安全地将List&lt;Child&gt; 转换为List&lt;Parent&gt;,因为它会让您将Apple 添加到List&lt;Banana&gt;

      您可以改为创建List&lt;FuitPart&gt;

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

      【讨论】:

        【解决方案4】:

        由于List&lt;BananaPart&gt; 没有继承List&lt;FruitPart&gt;,你不能那样做。

        我建议您创建一些包装类,例如 FruitedDetailsBananaDetails 来继承它。这样您就可以封装您正在使用的类的具体细节,而不必在打包列表时处理各种对象转换。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-04-19
          • 1970-01-01
          • 2021-10-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多