C# 对象的返回内部组件

问题描述 投票:0回答:1

目标

我想在 C# 中实现的一个非常常见的设计如下:一个类,拥有另一个类的多个实例。

为了清楚起见,让我们举个例子,假设一辆拥有四个“轮子”的“汽车”。

以下是我想要满足的约束:

  • 我希望汽车显示其车轮,而不是隐藏它们。 => 汽车类中应该有类似的东西

    public Wheel GetWheel(Left, Rear)
    
  • 我希望汽车能够以某种方式更新其车轮。例如,在汽车类中可以有这样的方法:

    public void ChangeTires(TireType)
    
  • 我不希望其他人能够更新轮子。例如,不应该运行类似的东西:

    aCar.GetWheel(Left, Rear).SetTire(someTireType); // Does not compile
    

这是一个非常非常重复的方案:网络有节点,椅子有靠背,用户有凭证,套接字有IP地址,银行帐户有所有者......

我阅读论坛并询问同事他们是如何做到这一点的。我得到了多个答案。他们在这里。

我的问题是:

  • 还有上面未列出的其他方法来实现这个目标吗?
  • 对于“好”的做法是否有普遍共识?
  • 是否有关于该主题的记录反映?制作 C# 或广泛使用它的聪明人可能很久以前就解决了这个话题。

解决方案尝试1:使用

internal

有些人建议限制

SetTire
方法对当前程序集的可见性:

class Wheel
{
   internal void setTire(TireType someTireType);
}

只有Wheel之外的“同一组件中的文件”才能更新它(更换轮胎)。

但这意味着 Car 和 Wheel 是在同一个程序集中定义的,这通过传递性使得所有内容都在同一个程序集中定义。

另外,仅仅将其限制在同一个程序集中是不够的,我想将其限制在

Car

解决方案尝试2:返回副本

有些人建议

GetWheel
方法应该返回一个副本:

class Car
{
   public Wheel LeftRearWheel;

   public Wheel GetWheel(LeftOrRight, FrontOrRear)
   {
       return LeftRearWheel.DeepCopy();
   }
}

使用这个解决方案,客户端代码不会知道轮子是一个副本。这意味着,每次客户端编码器编写 get 时,他不知道是否可以更新返回的对象。

此外,深度复制应用程序中使用的所有内容可能会引起性能问题。

换句话说:以下代码可以编译,但不执行任何操作:

aCar.GetWheel(Left, Rear).SetTire(someTireType); // Compiles but does nothing

解决方案尝试3:创建一个不可变的轮子

有些人建议这样做:

public class ReadonlyWheel
{
    public Tire getTire();
}

internal class Wheel : ReadonlyWheel
{
    internal void setTire(Tire);
}

public class Car
{
    public Wheel LeftRearWheel;

    public ReadonlyWheel GetWheel(LeftOrRight, FrontOrRear)
    {
        return LeftRearWheel;
    }
}

一个变体是声明一个接口

public interface IWheel
{
    TireType getTire();
}

internal class Wheel: IWheel
{
    public TireType getTire();
    internal void setTire(Tire);
}

public class Car
{
    public Wheel LeftRearWheel;

    public IWheel(LeftOrRight, FrontOrRear)
    {
        return LeftRearWheel;
    }
}

这个解决方案确实实现了目标,但意味着大量的代码重复和额外的复杂性。由于这是一种非常频繁出现的模式,我想避免代码重复。

解决方案尝试4:不在乎

有些人声称这个问题本身超出了“c# 思维方式”。

根据一些人的说法,确保我的用户不会用我的模型做“愚蠢”的事情并不是我所关心的。

一个变体是:“如果发生这种情况,那就意味着你的模型很糟糕”。轮子应该是完全私有的或完全公开的。

我无法让自己这样想。也许有人可以指出任何解释“c# 思维方式”如何实现这种建模的文档。

解决方案尝试5:使用ReadOnlyCollection

如果我想显示的对象是一个容器,则有一个名为

ReadOnlyCollection
的类,据我了解,它结合了解决方案尝试 2 和 3 :它创建一个副本并将其嵌入到只读接口中:

class Car
{
    private List<Wheel> myWheels;

    public ReadOnlyCollection<Wheel> GetWheels()
    {
        return myWheels.AsReadOnly();
    }
}

它实现了部分目标,即警告用户不要尝试更新集合。但它无法阻止更新。换句话说,以下代码编译后不执行任何操作。

aCar.GetWheels()[0].SetTire(someTireType); // Compile but does nothing

此外,不适用于非收藏品。

c# oop constants immutability
1个回答
0
投票

我不确定我是否理解这个问题,但我会尝试总结我所理解的内容:

  • Car
    应该容纳一些
    Wheels
  • Car
    应该有一个
    ChangeTires
    方法
  • 呼叫者不能在
    ChangeTires
    上呼叫
    Wheel
    - 只能通过
    Car

如果是这样,一种方法是将公共接口与实现分开。

首先,定义您想要向客户端代码公开的公共 API:

public interface IWheel
{
    // Put some members here that DON'T include changing tires...
    // Current pressure, size, etc.
}

现在创建

Car
类,并使
IWheel
实现成为
Car
内部的私有嵌套类:

public sealed class Car
{
    private readonly Wheel leftRearWheel = new Wheel();

    public IWheel LeftRearWheel { get { return leftRearWheel; } }
    // Other wheels here...

    public void ChangeTires(TireType tireType)
    {
        leftRearWheel.ChangeTire(tireType);
        // Other wheels here...
    }

    private sealed class Wheel : IWheel
    {
        public void ChangeTire(TireType tireType)
        {
            // Change the tire
        }
    }
}

虽然嵌套

Wheel
类的
ChangeTire
方法被标记为
public
,但它仅对
Car
类可见,因为
Wheel
类是一个
private
类。

因此,

Car
可以在所有
ChangeTire
对象上调用
Wheel
,但其他类不能。

© www.soinside.com 2019 - 2024. All rights reserved.