Подтвердить что ты не робот

Как избежать открытого доступа к закрытым полям?

Например, пусть объявляет:

private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings
{
    get
    {
        return _strings;
    }
}

И теперь мы можем сделать следующее:

((List<string>) obj.Strings).Add("Hacked");

Таким образом, мы действительно не скрываем List от использования, а только скрываем его за интерфейсом. Как скрыть список в таком примере без копирования _strings в новой коллекции, чтобы ограничить модификацию списка?

4b9b3361

Ответ 1

Множество способов.

Самый наивный подход - всегда копировать личное поле:

return _strings.ToList();

Но это вообще не обязательно. Это будет иметь смысл, если:

  • Вы знаете, что код вызова обычно хочет List<T> в любом случае, поэтому вы можете также вернуть его (не подвергая оригиналу).
  • Для открытого объекта важно сохранить исходные значения, даже если исходная коллекция впоследствии будет изменена.

В качестве альтернативы вы можете просто открыть оболочку только для чтения:

return _strings.AsReadOnly();

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

Эти стратегии также могут быть объединены. Если вам нужна копия оригинальной коллекции (так что изменения в оригинале не имеют эффекта), но также не хотят, чтобы возвращаемый объект был изменен *, вы могли бы пойти с:

 // copy AND wrap
return _strings.ToList().AsReadOnly();

Вы также можете использовать yield:

foreach (string s in _strings)
{
    yield return s;
}

Это похоже на эффективность и компромиссы по первому варианту, но с отсроченным исполнением.

Очевидно, что ключевая вещь, которую следует рассмотреть здесь, - это то, как ваш метод действительно будет использоваться, и какую функциональность, именно, вы хотите обеспечить этим свойством.

* Хотя, честно говоря, я не уверен, почему вам все равно.

Ответ 2

Как насчет использования ReadOnlyCollection. Это должно исправить эту конкретную проблему.

private ReadOnlyCollection<string> foo;
public IEnumerable<string> Foo { get { return foo; } }

Ответ 3

Я просто отправлю вам мои мысли о мыслях об инкапсуляции, возможно, вы найдете их разумными.

Как известно, инкапсуляция - очень полезная вещь, она помогает бороться с сложностью. Это делает ваш код логически минималистичным и понятным.

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

Собственно, насколько я знаю, с использованием некоторого отражения почти нет ограничений, даже с полностью частными членами. Так почему бы вам не попытаться скрыть их?

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

private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings
{
    get
    {
        return _strings;
    }
}

Ответ 4

ReadOnlyCollection<T>: http://msdn.microsoft.com/en-us/library/ms132474.aspx

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

Ответ 5

вы можете обернуть свой список в getter так:

private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings {
  get {
    return new ReadOnlyCollection<string>(_strings);
  }
}

Таким образом, вы можете свободно изменять свой личный список внутри своего класса и не беспокоиться об изменениях вне.

ПРИМЕЧАНИЕ: ReadOnlyCollection не копирует список, который он содержит (например, _strings)

Ответ 6

Поскольку никто не предлагал это как ответ еще, вот еще одна альтернатива:

private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings 
{
    get { return _strings.Select(x => x); }
}

Он эффективно эквивалентен методу yield return, упомянутому ранее, но немного более компактному.

Ответ 7

Помимо ReadOnlyCollection есть действительно Неизменяемые коллекции для .NET.

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

public ImmutableList<string> Strings { get; private set; }

Подробнее: http://blogs.msdn.com/b/bclteam/archive/2012/12/18/preview-of-immutable-collections-released-on-nuget.aspx