【问题标题】:How does composition work in C#?组合在 C# 中是如何工作的?
【发布时间】:2017-11-08 06:17:20
【问题描述】:

我有一个汽车课:

public class Car
{    
    private String _Brand;
    private String _Model;
    private Engine _Engine;

    public Car(){

    }

     public String Brand
     {
        get { return _Brand; }
        set { _Brand = value; }
     }

     public String Model
     {
        get { return _Model; }
        set { _Model = value; }
     }

     public Engine Engine
     {
        get { return _Engine; }
        set { _Engine = value; }
     }
}

还有一个引擎类:

public class Engine
{
     private int _Code;
     private DateTime _Year;

     public Engine(){

     }

     public int Code
     {
        get { return _Code; }
        set { _Code = value; }
     }

     public DateTime Year
     {
        get { return _Year; }
        set { _Year = value; }
     }
}

Registration.aspx.cs 的代码隐藏中,我想使用按钮、汽车和引擎以及它们各自的关系(组合)来创建。

但是,我对这些对象的合成应该如何表现有很大的困惑。即,我不知道是否必须在 Car 的构造函数中声明:Engine = new Engine();,或者是否在注册事件(按钮,代码隐藏)中声明:Car car = new Car(),然后是 car.Engine = new Engine();

我不知道在这样的场景中组合关系是如何工作的。声明“new”的位置的存在让我陷入了各种概念的混杂。

问候

【问题讨论】:

    标签: c# asp.net constructor code-behind default-constructor


    【解决方案1】:

    你可以像下面这样重构你的类,以避免boilerplate code。我们只是使用了一个名为自动实现属性的功能。

    public class Engine
    {
        public int Code { get; set; }
        public DateTime Year { get; set; }
    }
    
    public class Car
    {    
        public string Brand { get; set; }
        public string Model { get; set; }
        public Engine Engine {get; set; } 
    }
    

    现在请注意删除了多少行代码。

    关于您的问题,您可以使用 object initializer 创建一个 Car 类型的对象,如下所示:

    var car = new Car 
    {
        Brand = "the brand name";
        Model = "the model name";
        Engine = new Engine
        {
            Code = "the code engine";
            Year = new DateTime(2017,1,1); // the year the engine manufactured.
        }
    }
    

    我建议您阅读这两个特性,自动实现的属性对象初始化器。下面有两个关于这些功能的链接。

    【讨论】:

    • 这并没有解释什么是 Car 和 Engine 之间的组合,也没有解释为什么组合不是这个特定问题的最佳选择。
    • @DesertFox 我根本没有提到composition的概念,因为我认为OP的问题实际上不是什么是composition以及它是如何在C#中实现的,但是你如何实例化一个自定义类型的对象,它具有另一个自定义类型的属性。
    【解决方案2】:

    这完全取决于您设计课程的方式。汽车和发动机非常通用。例如,如果您要创建一辆 Mercedes,那么创建一个继承自 Car 的新类型 MercedesCar 是有意义的。在这种情况下,您可以在 MercedesCar 的构造函数中实例化一个 MercedesEngine,如下所示:

    public class Car
    {
        public Engine Engine
        {
            get;
            private set;
        }
    
        public Car(Engine engine)
        {
            this.Engine = engine;
        }
    }
    
    public class MercedesCar : Car
    {
        public MercedesCar() : base(new MercedesEngine())
        {
        }
    }
    
    public class Engine
    {
    }
    
    public class MercedesEngine : Engine
    {
    }
    

    但如果你只坚持使用Car类,那么从外部创建引擎并没有错!

    【讨论】:

    • 是的。我只是用它作为例子(顺便说一下不是最好的例子......)来清除它。谢谢!!!
    【解决方案3】:

    这取决于您希望如何控制 Engine 属性的可访问性/使用。

    案例 1: 如果您希望 Car 完全控制 Engine 属性并且没有其他人可以访问它,请将其设为私有字段并在 Car 构造函数中实例化。

    public class Car
    {
        private readonly Engine _engine;
        public Car()
        {
            _engine = new Engine();
        }               
    }
    

    案例 2: 引擎应该可以从外部访问,以便任何人都可以设置或访问它。 它可以通过两种方式完成:

    1) 构造函数注入 - 通过构造函数注入依赖,通常使用接口抽象,以便轻松完成模拟/单元测试。

    public class Car
    {
        public Car(IEngine engine)
        {
            Engine = engine;
        }
        public IEngine Engine { get; set; }
    }
    var car = new Car(new Engine());
    

    2) 手动设置引擎属性

    public class Car
     {  
        public Engine Engine { get; set; }           
     }
    var car = new Car();
    car.Engine = new Engine();
    

    【讨论】:

    • 但是,如果我在 Car 构造函数中实例化它。我应该在按钮事件(代码隐藏)中实例化引擎吗?。
    • 你不需要。引擎是汽车私有的,当 Car 被实例化时,将由 Car 处理。
    • 你这样说:Car.Engine.Code = txtCode.Text;?
    【解决方案4】:

    我已经讨论了这个级别的类架构已经有几年了,所以我会尽量不让自己听起来很愚蠢:)

    首先,我不认为我们在这里谈论组合:(在现实生活中)Engine 的生命周期不受Car 的约束/控制。如果我假设正确,那么这两个类都试图代表现实生活的映射。

    据我所知,实际上,您可以购买 EngineCar,但如果没有 EngineCar 将无法运行(但可以存在)(可能是那些新奇的特斯拉可以吗?)。

    那尖叫不是作曲,因为它没有描述所有权关系。

    根据您想要的严格程度,我将其称为聚合(更多)或关联(更少)。

    我个人更倾向于聚合,因为它比CarSpoiler 更牢固。

    因此,我的建议是:

    C#

    public class Engine
    {
         public Engine() { ... }     
    
         public int Code { get; set; }
    
         public DateTime Year { get; set; }
    }
    
    public class Car
    {
         public Car(Engine engine) : this() { 
              this.Engine = engine;
         }
    
         public Car() {
         } 
    
         public string Brand { get; set; }
    
         public String Model { get; set; }
    
         public Engine Engine { get; set; }
    }
    

    代码隐藏

    var engine = new Engine();
    var car = new Car(engine);
    

    Car 类定义是说您可以创建带有或不带有 EngineCar,但对我来说,它强调了 EngineCar 确实很重要,因此构造函数接受Engine 可用。

    关于作曲

    Car 是“整体”和Engine 是“部分”的组合意味着Engine 生命周期完全由Car 控制。在这种情况下,您基本上是在声明“在创建 Car 的新对象时,该对象将在内部创建一个新的 Engine,该 Engine 将在不再使用 Car 对象时被释放。”

    代码如下所示:

    //still the same
    public class Engine
    {
         public Engine() { ... }     
    
         public int Code { get; set; }
    
         public DateTime Year { get; set; }
    }
    
    public class Car
    {
         public Car() {
            //_engine = new Engine();
         }
    
         //Engine initialisation can be either here or inside the constructor (check constructor's commented code)
         private readonly Engine _engine = new Engine();
    
         public string Brand { get; set; }
    
         public String Model { get; set; }
    
         //you shouldn't be able to assign an _engine from outside
         public Engine Engine { get { return _engine; } }
    }
    

    组合代码隐藏

    var car = new Car();
    car.Engine.Code = 123;
    

    【讨论】:

    • 你是对的。这更像是一个聚合而不是一个组合,我的错。不管这两者的关系如何。我关心的是如何处理组合以及通过正确应用组合来声明“新”的位置。类的“新”和代码隐藏的“新”弄乱了我的设计。是的,我知道,是我的错。
    • 所以在 Code-Behind 中(说到组合)。我不应该声明:Engine engine = new Engine(),而是我做一个: Car car = new Car();在它下面,Car.Engine.Brand = txtBrand.Text?.
    • 是的,在作文的情况下就好了。
    • 别担心,伙计,很乐意提供帮助(并重温我很久以前使用的一些概念)。如果您喜欢这个答案,请随时标记为您问题的答案/给予“支持”
    猜你喜欢
    • 1970-01-01
    • 2012-03-31
    • 2016-06-02
    • 2018-03-25
    • 2021-02-09
    • 1970-01-01
    • 2011-01-05
    • 2011-07-03
    • 1970-01-01
    相关资源
    最近更新 更多