Можно ли возвращать массив или список как свойство в .NET? - PullRequest
23 голосов
/ 10 августа 2011

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

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

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

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

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

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

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

Ответы [ 6 ]

10 голосов
/ 11 августа 2011

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

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

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

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

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

ОБНОВЛЕНИЕ Вы также можете вернуть итератор, я бы не стал возвращать IEnumerable в качестве приведения суперкласса, поскольку он может быть приведен обратно, но если вместо этого вы возвращаете итератор (например, с помощью Skip (0)) вы в безопасности (если не считать возможности изменять содержащиеся объекты, конечно).

Например:

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

Лучше, чем:

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

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

2 голосов
/ 11 августа 2011

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

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

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

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

2 голосов
/ 11 августа 2011

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

Однако самое простое, что нужно сделать для защиты инкапсуляции массива, - это открыть только 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, то нет возможности изменить внутреннее состояние, кроме как с помощью методов / свойств класса.

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

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

2 голосов
/ 11 августа 2011

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

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

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

1 голос
/ 11 августа 2011

Я не согласен с предпосылкой, что вы хотели бы 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.

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

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

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

1 голос
/ 11 августа 2011

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

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