Массивы или IEnumerable<>
От: nikda  
Дата: 22.06.16 08:58
Оценка:
Для публичных свойств класса и для параметров публичных функций, что предпочтительней использовать массивы или IEnumerable<>

public class A
{
  public int[] Items{ get; private set; }

  public decimal?[] Get(string[] strings)
  {
    return ...;
  }
}


или

public class A
{
  public IEnumerable<int> Items{ get; private set; }

  public IEnumerable<decimal?> Get(IEnumerable<string> strings)
  {
    return ...;
  }
}
Re: Массивы или IEnumerable<>
От: Doc Россия http://andrey.moveax.ru
Дата: 22.06.16 09:13
Оценка: +3
Здравствуйте, nikda, Вы писали:

N>Для публичных свойств класса и для параметров публичных функций, что предпочтительней использовать массивы или IEnumerable<>


Для параметров — наиболее общий подходящий интерфейс. В данном случае — IEnumerable

Для свойств и возвращаемых значений — по ситуации (по бизнес логике). Где-то удобнее массив, где-то IList или ICollection (подразумевая что в результат можно добавлять). А где-то IReadonlyCollection — показывая что список только для чтения. IEnumerable подойдет для списков, значения которых генерируются по мере итерации.

Возвращать IEnumerable для готовых массивов несколько опасно, т.к. не зная что "под капотом" метода пользователи интерфейса будут скорее всего делать ToList или ToArray чтобы избежать повторной итерации.
Re: Массивы или IEnumerable<>
От: Sinix  
Дата: 22.06.16 09:14
Оценка:
Здравствуйте, nikda, Вы писали:

N>Для публичных свойств класса и для параметров публичных функций, что предпочтительней использовать массивы или IEnumerable<>


Обычно для возвращаемых значений используется IReadOnlyList/Collection для свойств, массив / enumerable для методов.

Для параметров — по обстоятельствам, чаше всего IEnumerable<T>, плюс (как опция) перегрузка с массивом.
Отредактировано 22.06.2016 9:23 Sinix . Предыдущая версия .
Re: Массивы или IEnumerable<>
От: v6  
Дата: 22.06.16 16:48
Оценка: +1
Здравствуйте, nikda, Вы писали:

N>Для публичных свойств класса и для параметров публичных функций, что предпочтительней использовать массивы или IEnumerable<>


У Липперта был хороший пост на эту тему: https://blogs.msdn.microsoft.com/ericlippert/2008/09/22/arrays-considered-somewhat-harmful/

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

For this reason alone I do almost no programming with arrays anymore. Arrays simply do not model any problem that I have at all well – I rarely need a collection which has the rather contradictory properties of being completely mutable, and at the same time, fixed in size. If I want to mutate a collection it is almost always to add something to it or remove something from it, not to change what value an index maps to.

Re[2]: Массивы или IEnumerable<>
От: Sinix  
Дата: 22.06.16 17:13
Оценка: +2
Здравствуйте, v6, Вы писали:


v6>У Липперта был хороший пост на эту тему: https://blogs.msdn.microsoft.com/ericlippert/2008/09/22/arrays-considered-somewhat-harmful/


+1. Только надо учитывать, что с времени написания поста появились ещё варианты, от readonly collections и до ImmutableArray<T>.
Re[2]: Массивы или IEnumerable<>
От: nikda  
Дата: 27.06.16 13:58
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Здравствуйте, nikda, Вы писали:


N>>Для публичных свойств класса и для параметров публичных функций, что предпочтительней использовать массивы или IEnumerable<>


Doc>Для параметров — наиболее общий подходящий интерфейс. В данном случае — IEnumerable


Doc>Для свойств и возвращаемых значений — по ситуации (по бизнес логике). Где-то удобнее массив, где-то IList или ICollection (подразумевая что в результат можно добавлять). А где-то IReadonlyCollection — показывая что список только для чтения. IEnumerable подойдет для списков, значения которых генерируются по мере итерации.


Doc>Возвращать IEnumerable для готовых массивов несколько опасно, т.к. не зная что "под капотом" метода пользователи интерфейса будут скорее всего делать ToList или ToArray чтобы избежать повторной итерации.



Как я понял из середины ответа: для возвращаемых результатов функций — применять IList/ICollection/IEnumerable
Но в последнем абзаце написано прямо противоположное. Видимо Что-то я недопонял.
Re[3]: Массивы или IEnumerable<>
От: Sinix  
Дата: 27.06.16 14:12
Оценка:
Здравствуйте, nikda, Вы писали:

N>Как я понял из середины ответа: для возвращаемых результатов функций — применять IList/ICollection/IEnumerable

N>Но в последнем абзаце написано прямо противоположное. Видимо Что-то я недопонял.

Как всегда, если надо разобраться, то забиваем на чужое мнение и изучаем матчасть, в данном случае — Guidelines for Collections.

Учитываем, что FDG лет 6 как не обновлялись и, допустим, вот это

DO use Collection<T> or a subclass of Collection<T> for properties or return values representing read/write collections.
If Collection<T> does not meet some requirement (e.g., the collection must not implement IList), use a custom collection by implementing IEnumerable<T>, ICollection<T>, or IList<T>.

DO use ReadOnlyCollection<T>, a subclass of ReadOnlyCollection<T>, or in rare cases IEnumerable<T> for properties or return values representing read-only collections.
In general, prefer ReadOnlyCollection<T>. If it does not meet some requirement (e.g., the collection must not implement IList), use a custom collection by implementing IEnumerable<T>, ICollection<T>, or IList<T>. If you do implement a custom read-only collection, implement ICollection<T>.ReadOnly to return false.

с успехом заменяется встроенными IReadOnlyXyz
Re[3]: Массивы или IEnumerable<>
От: Doc Россия http://andrey.moveax.ru
Дата: 27.06.16 16:51
Оценка: 4 (1) +1
Здравствуйте, nikda, Вы писали:

N>Как я понял из середины ответа: для возвращаемых результатов функций — применять IList/ICollection/IEnumerable


Верно. Плюс IReadOnlyCollection и подобные

N>Но в последнем абзаце написано прямо противоположное. Видимо Что-то я недопонял.


Это уточнение про IEnumerable. Его стоит использовать только если значения списка вычисляются по мере итерации. Например (утрируя):
public IEnumerable<int> GetSequence (int start, int end) 
{
    while (start < end)
        yield return start++;
}


но

public IReadOnlyCollection<int> GetSequence (int start, int end) 
{
    int[] values = this.GetFromDb(start, end);

    return values;
}


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

Во втором вся последовательность полностью располагается в памяти перед return. Тут подходят IList/ICollection/IReadOnly*
Re[4]: Массивы или IEnumerable<>
От: nikda  
Дата: 28.06.16 06:41
Оценка:
Здравствуйте, Doc.
Спасибо за ответ.

Допусти есть такой код, нормально ли в нём использовать IEnumerable или нужно заменить на IReadOnlyCollection в строках 1, 2, 3
А также в Main при вызове членов класса A, например при вызове конструктора нужно ли делать ToList() (например для избежания двойного перебора коллекции) или это нужно смотреть в каждом конкретном случае.


public class A : IEnumerable<Record>
{
    private IEnumerable<Record> _Items; // 1
    
    public IEnumerable<Record> Items // 2
    {
        get
        {
            return _Items;
        }
    }
    
    public A(IEnumerable<Record> srcItems)
    {
        _Items = srcItems;
    }
    
    public IEnumerator<Record> GetEnumerator()
    {
        return _Items;
    }
    
    public IEnumerator<Record> GetProcessedItems(int p1, string p2, ...) // 3
    {
        IEnumerator<Record> res = new List();
    
        foreach(Record r in _Items)
        {
            // Что-то делаем с исходными объектами или получаем новые на основе исходных
            // if (...) // Добавляем некоторые в список
            //   res.Add(r)
        }
        
        return res;
    }    
}

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<Record> items = GetItemsFromXXX();
        
        A a = new A(items); //4
        
        foreach(Record r in a)
        {
            Console.WriteLine(r);
        }
    
        IEnumerator<Record> resItems = a.GetProcessedItems(1, "aaa"); // 5
        
        foreach(Record r in resItems)
        {
            Console.WriteLine(r);
        }
    
    }    
}
Re[5]: Массивы или IEnumerable<>
От: Lexey Россия  
Дата: 28.06.16 12:29
Оценка:
Здравствуйте, nikda, Вы писали:

N>Допусти есть такой код, нормально ли в нём использовать IEnumerable или нужно заменить на IReadOnlyCollection в строках 1, 2, 3


Как минимум 1 лучше изменить, ибо неизвестно, что у исходного IEnumerable под капотом. При неоднократных обращениях можно получить как просто тормоза, так и разные данные (если там обращение к базе, например) или еще что похуже.

N>А также в Main при вызове членов класса A, например при вызове конструктора нужно ли делать ToList() (например для избежания двойного перебора коллекции) или это нужно смотреть в каждом конкретном случае.


Лучше в конструктор передавать IReadOnlyList или IReadOnlyCollection. И его же хранить в 1. И возвращать его же.
Для GetPtocessedItems смысла в возврате IEnumerable при такой реализации тоже не наблюдается.
"Будь достоин победы" (c) 8th Wizard's rule.
Re[6]: Массивы или IEnumerable<>
От: nikda  
Дата: 28.06.16 13:11
Оценка:
Здравствуйте, Lexey, Вы писали:

L>Здравствуйте, nikda, Вы писали:


N>>Допусти есть такой код, нормально ли в нём использовать IEnumerable или нужно заменить на IReadOnlyCollection в строках 1, 2, 3


L>Как минимум 1 лучше изменить, ибо неизвестно, что у исходного IEnumerable под капотом. При неоднократных обращениях можно получить как просто тормоза, так и разные данные (если там обращение к базе, например) или еще что похуже.


Допустим я заменю на IReadOnlyCollection, тогда нужно в конструкторе преобразовывать srcItems в конкретный тип? Тогда в какой, например в список?
_Items = srcItems.ToList(); // _Items  - IReadOnlyCollection


N>>А также в Main при вызове членов класса A, например при вызове конструктора нужно ли делать ToList() (например для избежания двойного перебора коллекции) или это нужно смотреть в каждом конкретном случае.


L>Лучше в конструктор передавать IReadOnlyList или IReadOnlyCollection. И его же хранить в 1. И возвращать его же.

L>Для GetPtocessedItems смысла в возврате IEnumerable при такой реализации тоже не наблюдается.

IEnumerable<Record> — возвращал как минимальную необходимый тип, т.к. результат используется только для итераций.
Re[7]: Массивы или IEnumerable<>
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 28.06.16 14:03
Оценка: +1 -1
Здравствуйте, nikda, Вы писали:

N>IEnumerable<Record> — возвращал как минимальную необходимый тип, т.к. результат используется только для итераций.


Возвращать надо, в первом приближении, наоборот, самый богатый публичный тип. А вот принимать — самый бедный.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[8]: Массивы или IEnumerable<>
От: nikda  
Дата: 28.06.16 14:23
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, nikda, Вы писали:


N>>IEnumerable<Record> — возвращал как минимальную необходимый тип, т.к. результат используется только для итераций.


AVK>Возвращать надо, в первом приближении, наоборот, самый богатый публичный тип. А вот принимать — самый бедный.


Спасибо.
Вот в конструкторе принимаю самый бедный тип (IEnumerable<T>), затем, для внутреннего хранения, его нужно преобразовать в какой-то конкретный тип, например список List<T>, а у же из функций возвращать этот список или IReadOnlyCollection. Так?
Re[8]: Массивы или IEnumerable<>
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 28.06.16 15:43
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, nikda, Вы писали:


N>>IEnumerable<Record> — возвращал как минимальную необходимый тип, т.к. результат используется только для итераций.


AVK>Возвращать надо, в первом приближении, наоборот, самый богатый публичный тип. А вот принимать — самый бедный.


Образовался даунвоутер, но без объяснений.

Но Андрей прав: с точки зрения дизайна, в общем случае правило именно такое: принимать базовый тип, возвращать наиболее специфичный. Это правило может нарушаться, если речь идет о внутреннем (internal/private) коде и особенно если он касается перф. критикал мест. В этом случае разумно принимать специфичный тип ибо это проще в реализации и всякие енемерации не приведут к появлению мусора в куче.

У этого правила есть и исключения. Например, когда возвращаемый тип является сильно специфичным и/или подрывает инкапсуляцию. Но это вообще касается возврата любых коллекций, которые поинтят на внутреннее состояние объекта.
Например, возвращать словарь — это плохо почти всегда, поскольку инкапсуляция — бай-бай.

В результате можно уточнить правило и оно начнет звучать так:

Тип аргументов:
— Для закрытых/внутренних типов — любой. Можно начинать с конкретного типа и потом рефакторить в базовый тип.
— Для открытых типов — наиболее базовый, но только если это не приводит к необходимости вызовов ToList/ToArray внутри и код не является перф-критикалом.

Тип возвращаемого значения:
— Для "чистых методов" — наиболее конкретный, ибо клиент сможет воспользоваться всеми плюшками результата. Зачем у него их отбирать.
— Для свойств/методов, возвращающих внутреннее представление: возвращать readonly представление, ибо инкапсуляции придет полный ёк. И сразу подумать о том, чтобы спрятать внутреннее представление и полностью убрать подобные свойства/методы, ибо даже рид-онли представление эту самую инкапсуляцию подрывает. Подумать о выделении методов, которые будут выполнять полезную работу внутри, обращаясь к внутренним полям-коллекциям.

Хотя, в этом обсуждении не хватает контекста.

Камрад Мартин, который Фаулер, выделяет особое множество открытых типов — "опубликованные" типы (см. его статью Public versus Published Interfaces). Тут нужно понимать, что даже если тип открытый, но он используется в рамках одного проекта, то он не является опубликованным и вы имеете полный контроль над его рефакторингом. В этом случае требования к дизайну ослабевают и к данному API все еще применяются требования "внутреннего" кода. Если же код — в активном реюзе, вот тогда требования к дизайну резко повышаются и никакие шалости в виде выставления словарей и списков уже не простительны (хотя это все еще абсолютно ок для "чистых" функций).
Отредактировано 28.06.2016 15:44 SergeyT. . Предыдущая версия .
Re[9]: Возвращать наиболее конкретный
От: Qbit86 Кипр
Дата: 28.06.16 15:56
Оценка:
Здравствуйте, SergeyT., Вы писали:

ST>- Для "чистых методов" — наиболее конкретный, ибо клиент сможет воспользоваться всеми плюшками результата. Зачем у него их отбирать.


А зачем у себя отбирать гибкость? Решение возвращать конкретный тип вместо абстрактного может стать необратимым. Переход же от более абстрактного к более конкретному типу возвращаемого значения — лёгкое решение, оно не ломает существующий вызывающий код.

Нужно минимизировать принятие необратимых решений, если ситуация не форсирована.
Глаза у меня добрые, но рубашка — смирительная!
Re[10]: Возвращать наиболее конкретный
От: Qbit86 Кипр
Дата: 28.06.16 16:01
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>А зачем у себя отбирать гибкость?


Я к тому, что рассуждения по аналогии с ко- и контравариантными позициями в сигнатурах функций и «направлением стрелок» между абстракциями здесь не факт что применимо, евпочя.
Глаза у меня добрые, но рубашка — смирительная!
Re[11]: Возвращать наиболее конкретный
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 28.06.16 16:22
Оценка: +1
Здравствуйте, Qbit86, Вы писали:

Q>>А зачем у себя отбирать гибкость?


Q>Я к тому, что рассуждения по аналогии с ко- и контравариантными позициями в сигнатурах функций и «направлением стрелок» между абстракциями здесь не факт что применимо, евпочя.


Все сводится к тому, что это за функции — опубликованные или нет. Если это неопубликованные (см. пред. сообщение), то гибкость не теряется: меняется реализация метода, меняется тип возвращаемого значения, фиксятся клиенты. Если это какая-то внутренняя либа — то дело другое, да и в этом случае гибкость не уходит.

Есть такая проблема как преждевременное обобщение — когда разработчик пытается "угадать", что нужно клиентам его класса/модуля. Обычно эти дагадки неверны. Именно в этом заключается идея supple дизайна из DDD — начни с прямолинейного решения, которое хорошо делает свое дело и подточи напильником, когда в этом появится необходимость. Именно поэтому разумно выставлять из чистых методов массивы/листы. С ними проще работать. А гибкость? Гибкость никуда не денется. Но добавлять ее нужно тогда, когда видно, что она нужна.
Re[9]: Массивы или IEnumerable<>
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 28.06.16 16:59
Оценка:
Здравствуйте, nikda, Вы писали:

N>Вот в конструкторе принимаю самый бедный тип (IEnumerable<T>), затем, для внутреннего хранения, его нужно преобразовать в какой-то конкретный тип, например список List<T>, а у же из функций возвращать этот список или IReadOnlyCollection. Так?


Сложно говорить без конкретного кода, но примерно так.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[9]: Массивы или IEnumerable<>
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 28.06.16 16:59
Оценка: +1
Здравствуйте, SergeyT., Вы писали:

ST>- Для свойств/методов, возвращающих внутреннее представление: возвращать readonly представление, ибо инкапсуляции придет полный ёк.


Или копию.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[10]: Возвращать наиболее конкретный
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 28.06.16 17:02
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>А зачем у себя отбирать гибкость? Решение возвращать конкретный тип вместо абстрактного может стать необратимым. Переход же от более абстрактного к более конкретному типу возвращаемого значения — лёгкое решение, оно не ломает существующий вызывающий код.


Поправить тип возвращаемого значения не так уж и сложно, да и не так часто такое приключается. А вот код, который зовет Count() на IEnumerable, при том что в реальности там внутри массив или список — вот такое встречается сплошь и рядом.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.