Капсулиране на референтни типове в колекция

Декларирах клас с няколко свойства

class Soil
{
  public double AnglePhi { get; set; }
  public double AngleDelta { get; set; }
  .
  .
  .
}

Сега, за да манипулирам колекция от тях, създадох друг специален клас, само поради тази причина.

class Soils
{
  private const Byte numberOPredefined = 10;   
  private IList<Soil> soils;

  public Soil this[ushort i]
  {
    get { return new Soil() { AngleDelta = soils[i].AngleDelta, ... }; }
    set { if (i > numberOPredefined) soils[i] = value; }
  }
  .
  .
  .
}

Логиката зад това е да се защити донякъде от директна манипулация на свойствата на всеки екземпляр на почвата. Дайте копие в getter, поискайте "цял" почвен обект в setter.

От това, което разбрах досега, други решения биха могли да бъдат:
да направи класа на почвата неизменен,
да върне списък само за четене (но тогава референтните типове вътре могат да бъдат манипулирани)
да превърне класа на почвата в struct (просто),
увеличете класа на почвата с известна логика (методи и т.н.).

Бих искал да попитам дали горното "решение" изобщо има някаква стойност или е зле дефинирано.

Според мен това е типична ситуация, например да имате колекция от референтни типове и да искате да ги капсулирате. Каква е типичната рамка на мислене в тези ситуации?

РЕДАКТИРАНЕ:
Добре, след като прочетох отговорите, промених решението за това

class Soil
{
  private readonly double _AnglePhi;
  public double AnglePhi { get { return _AnglePhi; } }

  private readonly double _AngleDelta;
  public double AngleDelta { get { return _AngleDelta; } }
  .
  .
}

class SoilCollection
{
  private List<Soil> _Soils;
  public IList<Soil> Soils { get { return _Soils.AsReadOnly(); } }
  .
  .
}

Мисля, че класът Soil имаше нужда от логика вътре в себе си, а не вътре в друг клас. Ще пиша, ако открия недостатъци.


person Dimi_Pel    schedule 21.01.2011    source източник


Отговори (4)


Ако искате вашият тип Soil да има семантика на копиране, дефинирайте го като структура. Тогава трябва да го направите неизменен, като декларирате резервните полета като само за четене и добавите подходящ конструктор.

struct Soil
{
  private readonly double anglePhi;
  private readonly double angleDelta;

  public Soil(double phi, double delta) {
    this.anglePhi = phi;
    this.angleDelta = delta; 
  }

  public double AnglePhi { get { return anglePhi; } }
  public double AngleDelta { get { return angleDelta; } }
}

Ако го запазите като клас, не бих използвал индексатор за извличане на копия на обектите. Предпочитам да използвам метод, за да изясня, че потребителят получава копие на обекта. И го направете само за четене, точно като структурата по-горе, точно като клас. Това вероятно ще елиминира и необходимостта от правене на копия.

person Botz3000    schedule 21.01.2011
comment
Но както беше посочено, има няколко свойства в класа и това би нарушило насоките за поддържане на малки структури. - person Dimi_Pel; 21.01.2011

Можете да дефинирате Soil като ValueObject, след което той ще бъде неизменен след създаването:

class Soil
{
  public Soil(double anglePhi, double angleDelta)
  {
      AnglePhi = anglePhi;
      AngleDelta = angleDelta;
  }

  public double AnglePhi { get; private set; }
  public double AngleDelta { get; private set; }
}

И според мен е по-добре да преименуваме Soils на SoilCollection.

person Sergey Berezovskiy    schedule 21.01.2011
comment
Имате предвид ValueObject, както е обяснено в тази връзка? blogs.msdn.com/b/lucabol/archive/2007/12/03/ - person Dimi_Pel; 21.01.2011
comment
Е, не прочетох цялата статия, но ДА, според заглавието Стойностните обекти са обекти, чиято идентичност се основава на тяхното състояние, вместо на техния указател в паметта. Обект с неизменна стойност е обект на стойност, който не може да бъде променен. Не можете да промените състоянието му, трябва да създадете нови. - person Sergey Berezovskiy; 21.01.2011

Няма причина да трябва да прилагате и сетера, и гетера в класа Soil. Можете да изберете само да приложите get, което ще направи обектите Soil само за четене.

Очевидно ще трябва да имате някакъв друг метод за задаване на вътрешните стойности - може ли това да се направи в конструктор.

Например:

class Soil
{
    private double m_anglePhi;

    public Soil( double anglePhi )
    {
        m_anglePhi = anglePhi;
    }

    public double AnglePhi 
    {
        get { return m_anglePhi; }
    }
}
person Nick    schedule 21.01.2011

бих предложил:

1) Направете класа на почвата неизменен.

2) Направете клас Soils колекция само за четене. Както в, извлечете от IList и декларирайте методите add etc като изрична реализация на interfact.

person Manish Basantani    schedule 21.01.2011
comment
Благодаря ти за отговора Амби. - person Dimi_Pel; 21.01.2011