提问人:mgueydan 提问时间:9/23/2023 最后编辑:Andrew Mortonmgueydan 更新时间:9/24/2023 访问量:64
C# 对象返回的内部组件
C# object's returning internal components
问:
目标
我想在 C# 中实现的一个非常循环的设计如下:一个类,它拥有另一个类的多个实例。
为了清楚起见,让我们举个例子,假设一辆拥有四个“轮子”的“汽车”。
以下是我想要满足的约束:
我希望汽车展示它的车轮,而不是隐藏它们。=> 汽车类中应该有类似的东西
public Wheel GetWheel(Left, Rear)
我希望这辆车能够以某种方式更新它的车轮。例如,在 car 类中可以有如下方法:
public void ChangeTires(TireType)
我不希望其他人能够更新车轮。例如,应该不可能运行如下内容:
aCar.GetWheel(Left, Rear).SetTire(someTireType); // Does not compile
这是一个非常非常重复的方案:网络有节点,椅子有Back,用户有凭据,套接字有IPAddress,BankAccount有所有者......
我阅读了论坛,并询问同事他们是如何做到的。给了我多个答案。他们来了。
我的问题是:
- 有没有其他方法可以实现上面未列出的目标?
- 对于“好”的做法是否有普遍共识?
- 在某处是否有关于这个主题的记录反思?制作 C# 或广泛使用它的杰出人士可能在很久以前就解决了这个话题。
解决方案尝试 1:使用internal
有些人建议将该方法的可见性限制在当前程序集上:SetTire
class Wheel
{
internal void setTire(TireType someTireType);
}
只有与车轮相同的“组件中的文件”才能更新它(以更改轮胎)。
但它意味着 Car 和 Wheel 在同一个组件中定义,通过传递性,使所有内容都在同一组件中定义。
此外,将其限制为同一程序集是不够的,我想将其限制为 .Car
解决方案尝试 2:返回副本
有些人建议该方法应该返回一个副本:GetWheel
class Car
{
public Wheel LeftRearWheel;
public Wheel GetWheel(LeftOrRight, FrontOrRear)
{
return LeftRearWheel.DeepCopy();
}
}
使用此解决方案时,无需告诉客户端代码 wheel 是副本。这意味着,每次客户端编码人员编写 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
如果我想显示的对象是一个容器,那么有一个名为该类的类,据我所知,将解决方案尝试 2 和 3 结合起来:它创建一个副本并将其嵌入到只读接口中:ReadOnlyCollection
class Car
{
private List<Wheel> myWheels;
public ReadOnlyCollection<Wheel> GetWheels()
{
return myWheels.AsReadOnly();
}
}
它实现了目标的一部分,即警告用户他不应该尝试更新集合。但它无法阻止更新。换句话说,以下代码编译并且不执行任何操作。
aCar.GetWheels()[0].SetTire(someTireType); // Compile but does nothing
此外,它不适用于非集合。
答:
我不确定我是否理解这个问题,但我会尝试总结我所理解的内容:
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
评论
wheel.setTire
WheelMachine.SetTire(car, location, tire)