2016-01-21 56 views
3

将对象作为构造函数参数传递的最佳做法是什么?传递可变对象可能会导致意想不到的结果。使用可变对象作为构造函数参数

一个简单的例子。我们预计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的() - 它不能创造MyRectangle后更改variable1

选项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 body - 它不得使用可变对象:

public class MyRectangle 
{ 
    int _height; 
    int _width; 

    public MyRectangle(SomeMeasurements arg) 
    { 
     _height = arg.Height; 
     _width = arg.Width; 
    } 

    public int GetArea() 
    { 
     return _width * _height; 
    } 
} 

选项5SomeMeasurementsICloneable并用它在MyRectangle构造Clone()

任何这些选项都有缺陷 - 可能很难避免重复使用变量1,MyRectangle可能更复杂将其变成结构体,MyRectangle可能是外部的,您可能根本不会改变它,等等。解决这个问题的最正确方法是?

+0

另请参阅http://stackoverflow.com/questions/4327108/what-is-the-meaning-of-data-hiding –

回答

1

它取决于类之间的关系,以及它们设计的目的。

如果考虑从Stream实例构造一个StreamReader类,即Stream预计将继续以“这是自己的”可变类有自己的一套责任的同时,其以一定的方式易变性读者的交易。这两个对象之间存在持续的关系,如果在这里对Stream做了某些操作,则其中一个预计会影响读取器

在这种情况下,我们显然只是将参考传递给构造函数的Stream

在其他情况下,传递的对象表示正在创建的对象的初始状态。两者之间没有持续的关系。

这里最好复制通过的对象或其字段。 (当涉及到微选项时,复制字段会使初始构造稍微慢一些,并且它们的使用速度会稍快一些)。

你正在处理的是哪种情况是你正在设计的一部分,因为你可以决定让一个班级以任何方式工作。有些案例显然必须是其中的一种(在StreamReader的例子中,从来没有坚持要处理的Stream),但通常是有选择的。如果你仍然无法下定决心采用复制方式,那么在对象之间没有持续关系的情况下,如果你的依赖现在变得更简单,那么你应该赞同这个原则。

2

一般而言,您应该传递符合特定接口的服务,或仅在构造函数中传递不可变对象。如果要保护它免受外部更改,构造函数应该将传递给它的任何可变数据的副本。

一旦数据通过构造函数,它应该被认为是新实例状态的一部分,并且不应该在该实例之外进行修改。

你的选择3,4似乎最有用。选项2将解决问题,因为您将数据的副本传递给构造函数。在许多情况下,选项1可能无法控制。

+0

我得到200与选项2.我做错了什么? – enkryptor

+0

我认为你需要发布GetArea()的代码来回答这个问题 –

+0

我根本没有改变MyRectangle,我只是在SomeMeasurements – enkryptor

相关问题