2016-01-21 2 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. Fix TestMethod() - она ​​не должна изменять variable1 после создания MyRectangle.

вариант 2. Fix класс SomeMeasurements - превратить его в структуры:

public struct SomeMeasurements 
{ 
    public int Width { get; set; } 
    public int Height { get; set; } 
} 

вариант 3. Fix класс SomeMeasurements - сделать его неизменное:

public class SomeMeasurements 
{ 
    public SomeMeasurements(int width, int height) 
    { 
     Width = width; 
     Height = height; 
    } 

    public int Width { get; } 
    public int Height { get; } 
} 

вариант 4. Fix class 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. Сделать SomeMeasurementsICloneable и использовать его в MyRectangle конструктор Clone().

Любой из этих вариантов есть это недостатки - это может быть трудно, чтобы избежать повторного Variable1, MyRectangle может быть более сложным, чтобы превратить его в структуры, MyRectangle может быть внешним, и вы можете не менять его на всех, и т.д. Что такое самый правильный способ исправить это?

+0

См. Также http://stackoverflow.com/questions/4327108/what-is-the-meaning-of-data-hiding –

ответ

1

Это зависит от отношения между классами и того, что они предназначены для выполнения.

Если вы считаете StreamReader класса, построенный из Stream, например, что Stream, как ожидается, по-прежнему «это собственный» изменчивый класс со своим собственным набором обязанностей в то время как читатель имеет дело с его изменчивостью в данном пути. Между этими двумя объектами существует постоянная связь, и если вы делаете что-то с Stream здесь, то ожидает, чтобы повлиять на читателя.

В этом случае мы, очевидно, просто держим ссылку на Stream, переданную конструктору.

В других случаях передается объект, представляющий начальное состояние создаваемого объекта. Между ними нет постоянной связи.

Здесь лучше всего скопировать либо пройденный объект, либо его поля. (Когда дело доходит до микропотоков, копирование полей делает начальную конструкцию очень немного медленнее, а использование их очень немного быстрее).

В каком случае вы имеете дело с чем-то, что является частью того, что вы разрабатываете, тем, что вы можете решить сделать работу класса в любом случае. Некоторые случаи явно должны быть такими или другими (в примере StreamReader было бы бессмысленно никогда не держаться за Stream, с которым вы имели дело), ​​но часто есть выбор. Благоприятствуйте принципу наименьшего удивления, и если вы все еще не можете решиться на подход копирования, когда между объектами не существует постоянной связи, так как ваши зависимости теперь проще.

2

Как правило, вы должны передавать службы, соответствующие определенному интерфейсу или неизменяемым объектам только в конструкторах. Конструктор должен взять копию любых измененных данных, переданных ему, если вы хотите защитить их от внешних изменений.

После того, как данные пройдут через конструктор, он должен считаться частью состояния нового экземпляра и не должен быть доступен для модификации вне этого экземпляра.

Ваши варианты 3,4 кажутся наиболее полезными. Вариант 2 может решить проблему, потому что вы передаете копию данных в конструктор. Вариант 1 может оказаться вне вашего контроля во многих контекстах.

+0

Я получаю 200 с опцией 2. Что я делаю неправильно? – enkryptor

+0

Я думаю, вам нужно будет опубликовать код для GetArea(), чтобы ответить на этот вопрос –

+0

Я вообще не изменил MyRectangle, я просто изменил «класс» на «struct» в SomeMeasurements – enkryptor

Смежные вопросы