【问题标题】:How would I create a class that manages/alters an "inventory" of other classes?我将如何创建一个管理/更改其他类的“库存”的类?
【发布时间】:2021-03-08 21:25:08
【问题描述】:

我正在开发一个 EquipmentManager 类,该类需要能够查看所有 ShortTrailersLongTrailersTractors,基于唯一 ID 添加新的拖拉机/拖车,并删除拖拉机/拖车基于其 ID。

我不确定从哪里开始。我目前的困境是尝试在EquipmentManager 类中仅列出Trailer,该类可以结合ShortTrailerLongTrailer 的两个类。

EquipmentManager 类:

using System.Collections.Generic;

namespace BusinessLayer
{
    public class EquipmentManager
    {
        private List<Tractor> Tractors {get; set;}
        private List<ShortTrailer, LongTrailer> Trailers {get; set;}
        //          Incorrect but the idea^
        public EquipmentManager() //constructor
        {
            Trailers = new List<Trailer>();
            Tractors = new List<Tractor>();
        }

        public object GetTractorCount()
        {
            int TractorCount = 0;

            return TractorCount;
        }

        public object GetTrailerCount()
        {
            int TrailerCount = 0;

            return TrailerCount
        }

        public string ViewTractors(Tractors)
        {
            new List<Tractors> = " ";

            return Tractors;
        }

        public string ViewTrailers()
        {
            new string List<Trailers>;
            
            return Tractors;
        }

        public bool AddTractor()
        {

        }

        public bool RemoveTractor()
        {

        }

        public bool AddTrailer()
        {

        }

        public bool RemoveTrailer()
        {
            
        }
        
    }
}

Tractor、ShortTrailer 和 LongTrailer 的类分别在它们自己的 .cs 文件中:

namespace BusinessLayer
{
    public class Tractor
    {
        string ID;
        int[] Terminal = {01, 02, 03}; 
        string[] Status = {"Arrived", "Fueled", "Serviced", "Ready"};
    }

public class ShortTrailer
    {
        string ID;
        int[] Terminal = {01, 02, 03, 04, 05, 06, 07, 08, 09, 10};
        string[] Status = {"Arrived", "Fueled", "Serviced", "Ready"};
        int Length;
        int Width;
        int Height;
    }

public class LongTrailer
    {
        string ID;
        int[] Terminal = {01, 02, 03, 04, 05, 06, 07, 08, 09, 10};
        string[] Status = {"Arrived", "Fueled", "Serviced", "Ready"};
        int Length;
        int Width;
        int Height;
    }
}

【问题讨论】:

  • 这似乎是在乞求ITrailer 接口
  • 这是用于课堂项目还是用于工作?我问的原因是答案会根据用例的不同而有所不同。简而言之,您正在混合模型和逻辑。你的Manager 不应该有模型。它应该在模型类上运行。如果这是为了学校,使用Dictionary 将内容存储在内存中是相当简单的。如果没有,对于实现,您需要有类来将模型状态值保存在数据库中,例如 DynamoDb 等。
  • 短拖车和长拖车有什么区别?为什么他们需要有自己的课程?
  • @ColmPrunty 或者说实话IEntity
  • @seasharp 用一个基本示例更新了我的答案,以帮助澄清我指出的一些项目。祝你好运!

标签: c# list inheritance overloading


【解决方案1】:

我会在此处更改许多内容,以使您的解决方案更具可扩展性和可维护性。您需要研究以全面了解的项目是:

枚举

枚举的强大之处在于它们可以帮助我们以一致、通用的方式表示值,最重要的是,在编译时进行检查。从维护方面来看,它们可以帮助您从代码中删除幻数和字符串,从而节省您的一天(在严重的情况下是一周或一个月)。想象一下,您将Tractor 的状态设置为Arived 而不是Arrived,现在您的到达代码不起作用。枚举可帮助您防止此问题以及更多问题。我强烈建议将您的 Status 属性更改为 enum

public enum EquipmentStatus { None, Arrived, Fueled, Serviced, Ready }

接口

界面非常非常强大。它们允许我们在没有任何实现的情况下定义对象的外观。例如,您知道您的预告片都具有三个共同的属性lengthwidthheight。考虑到这一点,您可以使用接口定义预告片,并处理ITrailer,而不必单独考虑LongTrailerShortTrailer。不过,我不会对此主题进行太多详细介绍,因为我觉得我不能真正做到公正。

考虑到这一点,我建议创建一个接口来定义所有三个对象(IdTerminalStatus)中最常见的属性:

public interface IEquipmentObject {
    string Id { get; set; }
    int[] Terminal { get; set; }
    EquipmentStatus Status { get; set; }
}

泛型

泛型是神奇的怪物,如果使用得当,它可以帮助您完成您从未想过可能完成的任务。在您的情况下,下面关于单一责任的部分将更详细地介绍这一点。使用泛型,您可以在创建对象、调用方法等时指定数据类型,这非常有益。您已经在List&lt;T&gt; 中使用了泛型。表面上的好处是,您不必对设备对象进行装箱和拆箱,而是可以直接使用它们,因为您使用的是List&lt;Trailer&gt; 而不是List&lt;object&gt;

单一职责

我个人认为您试图通过单个对象定义完成太多工作。虽然从操作的角度来看它是有意义的,但从开发的角度来看它更容易分解。与其在一个单一的定义中创建一个同时处理所有三个的对象,不如创建一个以简化形式负责工作的单一定义:

public sealed class EquipmentCollection<T> : ICollection<T> where T : IEquimentObject {
    // See the basic example below.
}

从这里,我最终将创建具有EquipmentCollection 的各种实例的设备管理器对象:

public sealed class EquipmentManager {
    public EquipmentCollection<Tractor> Tractors { get; set; }
    public EquipmentCollection<Trailer> LongTrailers { get; set; }
    public EquipmentCollection<Trailer> ShortTrailers { get; set; }
}

EquipmentCollection&lt;T&gt; 定义上实现 ICollection&lt;T&gt; 时,您将设置添加、插入和删除对象的方法(以及与集合对象相关的许多其他功能)。这就是您需要引用您的Id 以确保您满足通过Id 添加和删除的要求。

基本示例

public enum EquipmentStatus { None, Arrived, Fueled, Serviced, Ready }
public enum TrailerType { None, Short, Long }
public class Terminal {
    public int Id { get; set; }
}
public interface IEquipmentObject {
    string Id { get; set; }
    Terminal Owner { get; set; }
    EquipmentStatus Status { get; set; }
}
public class Tractor : IEquipmentObject {
    public string Id { get; set; }
    public Terminal Owner { get; set; }
    public EquipmentStatus Status { get; set; }
}
public class Trailer : IEquipmentObject {
    public string Id { get; set; }
    public Terminal Owner { get; set; }
    public EquipmentStatus Status { get; set; }
    public TrailerType Type { get; set; }
    public int Length { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
}
public class EquipmentCollection<T> : ICollection<T> where T : IEquipmentObject {

    #region Fields

    private List<T> _items = new List<T>();

    #endregion

    #region ICollection Custom Implementation

    public bool Contains(T item) => _items.Any(containedItem => containedItem.Id == item.Id);
    public bool Remove(T item) => _items.Remove(_items.Single(removalCandidate => removalCandidate.Id == item.Id));

    #endregion

    #region ICollection Passthrough

    public int Count => _items.Count;
    public bool IsReadOnly => false;
    public void Add(T item) => _items.Add(item);
    public void Clear() => _items.Clear();
    public void CopyTo(T[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex);
    public IEnumerator<T> GetEnumerator() => _items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator();

    #endregion

}
public class EquipmentManager {
    public EquipmentCollection<Tractor> Tractors { get; set; }
    public EquipmentCollection<Trailer> ShortTrailers { get; set; }
    public EquipmentCollection<Trailer> LongTrailers { get; set; }
}

祝你好运!


另请参阅:sealedgeneric constraints

【讨论】:

    【解决方案2】:

    您可以在这里做几件事。

    你可以使用接口:

    public interface ITrailer
    {
        string ID;
        int[] Terminal;
        string[] Status;
        int Length;
        int Width;
        int Height;
    }
    
    public class ShortTrailer : ITrailer
    {
        public string ID { get; set; }
        public int[] Terminal { get; set; } = {01, 02, 03, 04, 05, 06, 07, 08, 09, 10};
        public string[] Status { get; set; } = {"Arrived", "Fueled", "Serviced", "Ready"};
        public int Length { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
    }
    
    public class LongTrailer : ITrailer
    {
        public string ID { get; set; }
        public int[] Terminal { get; set; } = {01, 02, 03, 04, 05, 06, 07, 08, 09, 10};
        public string[] Status { get; set; } = {"Arrived", "Fueled", "Serviced", "Ready"};
        public int Length { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
    }
    

    然后在您的 EquipmentManager 类中,您将拥有 public List&lt;ITrailer&gt; Trailers { get; set; }

    或者,如果您希望在您的预告片类中使用共享或唯一方法选项来共享属性,您可以使用abstract class

    public abstract class Trailer
    {
        public string ID { get; set; }
        public int[] Terminal { get; set; } = {01, 02, 03, 04, 05, 06, 07, 08, 09, 10};
        public string[] Status { get; set; } = {"Arrived", "Fueled", "Serviced", "Ready"};
        public int Length { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        
        public virtual void DoSomething()
        {
            // Do something generic for all trailers, but this method can be overridden by the child classes
        }
    }
    
    public class ShortTrailer : Trailer
    {
        public override void DoSomething()
        {
            // Do something short trailer version
        }
    }
    
    public class LongTrailer : Trailer
    {
        public override void DoSomething()
        {
            // Do something long trailer version
        }
    }
    

    然后在您的 EquipmentManager 类中,您将拥有 public List&lt;Trailer&gt; Trailers { get; set; }

    【讨论】:

    • TerminalStatus 使用enum 不是更好吗?
    • @Julia 是的,当然会,我考虑自己这样做,但这不是 OP 要求的,我不想让他们混淆。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-16
    • 2022-06-10
    • 1970-01-01
    相关资源
    最近更新 更多