将对象作为构造函数参数传递的最佳做法是什么?传递可变对象可能会导致意外结果。
一个简单的例子。我们期望200,但得到10000调用TestMethod():
public class Test
{
public int TestMethod()
{
var variable1 = new SomeMeasurements
{
Width = 10,
Height = 20
};
var obj1 = new MyRectangle(variable1);
// <... more code...>
variable1.Height = 1000; // a local variable was reused here, and it's field was changed
// <... more code...>
return obj1.GetArea();
}
}
public class SomeMeasurements
{
public int Width { get; set; }
public int Height { get; set; }
}
public class MyRectangle
{
SomeMeasurements _arg;
public MyRectangle(SomeMeasurements arg)
{
_arg = arg;
}
public int GetArea()
{
return _arg.Width * _arg.Height;
}
}
在这种情况下,错误很明显,但是对于更复杂的类,调试可能很繁琐。我想到了如何解决这个问题的几件事:
选项1。修复TestMethod() - 创建variable1
后不得更改MyRectangle
。
选项2。修复类SomeMeasurements
- 将其转换为结构:
public struct SomeMeasurements
{
public int Width { get; set; }
public int Height { get; set; }
}
选项3。修复类SomeMeasurements
- 使其成为不可变的:
public class SomeMeasurements
{
public SomeMeasurements(int width, int height)
{
Width = width;
Height = height;
}
public int Width { get; }
public int Height { get; }
}
选项4。修复类MyRectangle
正文 - 它不能使用可变对象:
public class MyRectangle
{
int _height;
int _width;
public MyRectangle(SomeMeasurements arg)
{
_height = arg.Height;
_width = arg.Width;
}
public int GetArea()
{
return _width * _height;
}
}
选项5。制作SomeMeasurements
ICloneable
并在Clone()
构造函数中使用MyRectangle
。
这些选项中的任何一个都存在缺陷 - 可能很难避免重复使用variable1,MyRectangle
可能会更复杂地将其转换为结构,MyRectangle
可能是外部的,您可能无法更改它什么是最正确的解决方法?
答案 0 :(得分:2)
通常,您应该传递符合某个接口的服务,或仅传递构造函数中的不可变对象。如果你想保护它免受外部变化的影响,构造函数应该获取传递给它的任何可变数据的副本。
一旦数据通过构造函数,它应该被视为新实例状态的一部分,并且不应该在该实例之外进行修改。
您的选择3,4似乎最有用。选项2可以解决问题,因为您将数据的副本传递给构造函数。在许多情况下,选项1可能无法控制。
答案 1 :(得分:1)
这取决于类之间的关系,以及它们的目的。
如果你考虑从StreamReader
实例构造的Stream
类,那么Stream
预期将继续是“它自己的”可变类,它有自己的一套责任,而读者以给定的方式处理其可变性。这两个对象之间存在持续的关系,如果在这里对Stream
做了一些事情,那么期望它会影响读者。
在这种情况下,我们显然只是对传递给构造函数的Stream
进行了引用。
在其他情况下,传递一个对象来表示正在创建的对象的初始状态。两者之间没有持续的关系。
这里最好复制传递的对象或其字段。 (当谈到微操作时,复制字段会使初始构造变得非常慢,并且使用它们的速度会稍微快一些。)
您正在处理的是您正在设计的内容,因为您可以决定让课程以任何方式运作。有些情况显然必须是一个或另一个(在StreamReader
示例中,没有理由永远不会坚持你正在处理的Stream
,但通常有一个选择。赞成最少惊喜的原则,如果您仍然无法决定复制方法,那么对象之间没有持续的关系,因为您的依赖关系现在变得更加简单。