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

Можно ли иметь массив или список, возвращаемый как свойство в .NET?

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

Если "Операция возвращает массив" использует метод (вместо свойства).

Страница находится здесь: Выбор между свойствами и методами

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

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

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

Использует свойство и просто возвращает ссылку на массив всегда плохой практике? Что делать, если вы хотите, чтобы вызывающий абонент мог изменять массив, или вас не волнует, изменяют ли они его? Это все еще плохо и почему, и если да, то каков был бы правильный способ разрешить вызывающему абоненту изменять?

4b9b3361

Ответ 1

Можете ли вы разрешить вызывающему абоненту изменять внутренний массив через свойство? Да, конечно, но вы возьмете множество возможных проблем. Как вы справляетесь с этими проблемами, и то, с чем вы можете жить, зависит от вас.

Совет MSDN правилен в очень строгом смысле. Тем не менее, я видел свойства List<T> и T[], возвращаемые ранее из классов. Если ваш класс - очень простой POCO, это не большая проблема, потому что тогда эти классы являются просто необработанными данными, и нет реальной бизнес-логики, чтобы повлиять.

Тем не менее, если я возвращаю список, и я не хочу, чтобы кто-то вошел во внутренний список, я либо возвращаю глубокую копию каждый раз, либо ReadOnlyCollection, или итератор. Например, есть много мест, в которых я кэширую запросы запросов веб-службы, и когда я возвращаю элемент кэша, я НЕ хочу, чтобы вызывающий пользователь изменял эти данные, или они будут изменять то, что я кэширую. Таким образом, я делаю глубокие копии (что еще быстрее, чем накладные расходы на вызов веб-службы).

Вам просто нужно знать, требует ли ваше использование безопасность или нет. Является ли класс только для внутреннего потребления? Или он предназначен для потребления более широкой аудиторией, и вы не представляете, что они собираются с ним делать? Эти вопросы могут повлиять на ваш ответ.

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

UPDATE Вы также можете вернуть итератор, я бы не возвращал IEnumerable в качестве суперкласса, потому что его можно отбросить, но если вы вернете вместо него итератор (например, с помощью Skip (0)), вы будете в безопасности ( кроме того, что все еще можно модифицировать содержащиеся объекты, конечно).

Например:

public IEnumerable<T> SomeList 
{
    get { return _internalList.Skip(0); }
}

Лучше, чем:

public IEnumerable<T> SomeList
{
    get { return _internalList; }
}

Так как позже все еще можно отбрасывать на List<T> или что бы это ни было, в то время как первый является итератором и не может быть изменен.

Ответ 2

Если внутреннее состояние вашего класса не зависит от массива или списка, который вы открываете, нормально его возвращать как есть. Поэтому не следует саботировать свой класс, изменяя список или массив.

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

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

Ответ 3

Это почти всегда плохо, но это не мешает людям делать это (включая меня). Это вопрос использования подходящего случая для сценария использования.

Однако простейшей задачей защиты инкапсуляции массива является разоблачение только IEnumerable или ICollection и методов для изменения внутреннего массива. См. Следующий пример. Я использовал List здесь, потому что семантика проще.

class YourClass {
    List<string> list = new List<string>();
    public IEnumerable<string> List {
        get { return list; }
    }
    public void Add(string str) {
        list.Add(str);
    }
}

В качестве возвращаемого типа IEnumerable/ICollection отсутствует возможность изменить внутреннее состояние, отличное от методов/свойств класса.

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

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

Ответ 4

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

Что касается того, что это "плохая практика", это в значительной степени связано с тем, что они описывают: даже если вы предоставляете только доступ для доступа к своему свойству, пользователь может все еще изменять содержимое массива и тем самым мутировать состояние вашего класса без вашего класса, имеющего способ проверки изменений - и, что хуже всего, это следствие дизайна не очень очевидно, если у вас уже достаточно глубокое знание семантики массива С#.

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

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

Ответ 5

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

Ответ 6

Я не согласен с предпосылкой, что вы хотите to preserve the internal array, you would have to return a deep copy of the array, not a reference to the array used by the property.

На самом деле это было бы довольно "astonishing" , если свойство действительно вернуло глубокую копию.

Иногда бывает, что вы хотите сохранить внутреннюю структуру и просто вернуть ее только для чтения (в форме IEnumerable<T>), но это ортогонально свойствам.

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