Здравствуйте, igna, Вы писали:
_FR>>Нет, наследование здесь не при чём. Дело просто в том, что этот конструктор не делает ничего такого, что нельзя было бы сделать без него (а конструктор с capacity делает). I>То, что "конструктор с capacity делает чего такого", не обязывает его быть открытым.
Но желание применить возможности, которые дает конструктор с capacity (а именно, оптимизация быстродействия), обязывает его быть открытым.
Здравствуйте, fmiracle, Вы писали:
F>Конструктор с указываемой Capacity интересен именно как открытый.
Он может быть закрытый и вызываться открытым статическим методом. Такая комбинация закрытого конструктора и открытого статического метода популярна, поэтому я сначала ответил кратко, без подробностей.
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, samius, Вы писали:
S>>Согласен, что так понятнее. Но исторически конструктор List<T>(IEnumerablt<T>) появился раньше метода ToList(). Кроме этого я подозреваю что сей конструктор мог предназначаться для наследников от List<T>. Я знаю, что List<T> не предназначался для наследования, но и sealed он не помечен. Да и AFAIR в msdn попадались примеры с наследованием от него.
_FR>Нет, наследование здесь не при чём. Дело просто в том, что этот конструктор не делает ничего такого, что нельзя было бы сделать без него (а конструктор с capacity делает).
Речь о методах AddRange(IEnumerable<T>) и InsertRange(Int32, IEnumerable<T>) ???
Если да, то что такого делает конструктор с capacity, чего нельзя было бы сделать через публичный сеттер Capacity?
_FR>А в [открытый] интерфейс типа без крайней необходимости вообще лучше не добавлять ничего, чего нельзя было бы сделать без имеющегося [открытого] интерфейса.
Что такого крайнего с capacity?
Здравствуйте, samius, Вы писали:
_FR>>Нет, наследование здесь не при чём. Дело просто в том, что этот конструктор не делает ничего такого, что нельзя было бы сделать без него (а конструктор с capacity делает). S>Речь о методах AddRange(IEnumerable<T>) и InsertRange(Int32, IEnumerable<T>) ???
Да даже без них на одном IList<>::Add можно было бы реализовать
S>Если да, то что такого делает конструктор с capacity, чего нельзя было бы сделать через публичный сеттер Capacity?
устанавливает все-лишь начальный размер. То есть код
var list = new List<X>(5);
отличается от
var list = new List<X>() { Capacity = 5, };
лишь тем, что во втором случае сначала будет создан массив с дефолтовым Capacity, а потом будет создан новый массив с требуемым capacity. В первом случае сразу же создаётся массив с нужным capacity. Это, конечно же, мелочи, о них я упомянул только для тех, кто любит придираться.
А по большому счёту, и от конструктора с capacity сейчас (при наличии object initializers) можно было бы отказаться.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, igna, Вы писали:
F>>Конструктор с указываемой Capacity интересен именно как открытый. I>Он может быть закрытый и вызываться открытым статическим методом. Такая комбинация закрытого конструктора и открытого статического метода популярна, поэтому я сначала ответил кратко, без подробностей.
Если этот статический метод вызывает ровно закрытый конструктор с теми же аргументами, что и у данного метода, то, совершенно очевидно, что данный статический метод — излишен.
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, samius, Вы писали:
_FR>>>Нет, наследование здесь не при чём. Дело просто в том, что этот конструктор не делает ничего такого, что нельзя было бы сделать без него (а конструктор с capacity делает). S>>Речь о методах AddRange(IEnumerable<T>) и InsertRange(Int32, IEnumerable<T>) ???
_FR>Да даже без них на одном IList<>::Add можно было бы реализовать
S>>Если да, то что такого делает конструктор с capacity, чего нельзя было бы сделать через публичный сеттер Capacity?
_FR>устанавливает все-лишь начальный размер. То есть код _FR>
_FR>var list = new List<X>(5);
_FR>
_FR>отличается от _FR>
_FR>var list = new List<X>() { Capacity = 5, };
_FR>
_FR>лишь тем, что во втором случае сначала будет создан массив с дефолтовым Capacity, а потом будет создан новый массив с требуемым capacity. В первом случае сразу же создаётся массив с нужным capacity. Это, конечно же, мелочи, о них я упомянул только для тех, кто любит придираться.
Я не любитель, но придется придраться.
Конструкторы с collection и capacity в одинаковом положении. Конструктор с collection позволяет избежать создания массива с дефолтовым Capacity в случае когда collection is ICollection<T>. Т.е. как и конструктор с capacity он делает что-то такое, что без него сделать нельзя (C) (см. выше выделенное)
_FR>А по большому счёту, и от конструктора с capacity сейчас (при наличии object initializers) можно было бы отказаться.
Уже нет. Легаси.
Здравствуйте, fmiracle, Вы писали:
F>Если этот статический метод вызывает ровно закрытый конструктор с теми же аргументами, что и у данного метода, то, совершенно очевидно, что данный статический метод — излишен.
Нет, боюсь примотаешься к какому слову — допишу еще немного — "и этот метод не делает ничего больше, кроме как вызов этого самого конструктора, который с теми же самыми аргументами".
Здравствуйте, fmiracle, Вы писали:
F>"и этот метод не делает ничего больше, кроме как вызов этого самого конструктора, который с теми же самыми аргументами".
То есть все же бывают случаи, когда конструктор с указываемой Capacity интересен не только как открытый.
Здравствуйте, samius, Вы писали:
S>Конструкторы с collection и capacity в одинаковом положении. Конструктор с collection позволяет избежать создания массива с дефолтовым Capacity в случае когда collection is ICollection<T>. Т.е. как и конструктор с capacity он делает что-то такое, что без него сделать нельзя (C) (см. выше выделенное)
Ну это же очень просто:
public static List<T> Create<T>(IEnumerable<T> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
var collection = source as ICollection<T>;
var list = (collection != null) ? new List<T>(collection.Count) : new List<T>();
foreach (var item in source)
{
list.Add(item);
}
return list;
}
тут никакого перераспределения памяти не будет, разница лишь (в некоторых случаях — когда известен Count) в ручной прокрутке source вместо Array.Copy(…).
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, fmiracle, Вы писали:
F>Но желание применить возможности, которые дает конструктор с capacity (а именно, оптимизация быстродействия), обязывает его быть открытым.
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, samius, Вы писали:
S>>Конструкторы с collection и capacity в одинаковом положении. Конструктор с collection позволяет избежать создания массива с дефолтовым Capacity в случае когда collection is ICollection<T>. Т.е. как и конструктор с capacity он делает что-то такое, что без него сделать нельзя (C) (см. выше выделенное)
_FR>Ну это же очень просто:
_FR>
_FR> public static List<T> Create<T>(IEnumerable<T> source)
_FR> {
_FR> if (source == null)
_FR> {
_FR> throw new ArgumentNullException("source");
_FR> }
_FR> var collection = source as ICollection<T>;
_FR> var list = (collection != null) ? new List<T>(collection.Count) : new List<T>();
_FR> foreach (var item in source)
_FR> {
_FR> list.Add(item);
_FR> }
_FR> return list;
_FR> }
_FR>
_FR>тут никакого перераспределения памяти не будет, разница лишь (в некоторых случаях — когда известен Count) в ручной прокрутке source вместо Array.Copy(…).
Ну вот, лишнего перераспределения избежать удалось, но эффективное копирование может организовать лишь конструктор List<T>(IEnumerable<T>). На больших размерах коллекций это сыграет куда сильнее, чем лишнее перераспределение. А InsertRange делает лишние копирования.
Здравствуйте, samius, Вы писали:
S>Ну вот, лишнего перераспределения избежать удалось, но эффективное копирование может организовать лишь конструктор List<T>(IEnumerable<T>).
А что в этом копировании неэффективного?
S>На больших размерах коллекций это сыграет куда сильнее, чем лишнее перераспределение.
Что ты называешь "больших"? Будет ли при указанных размерах вообще эффективным использование List<>? Ибо добавление одного элемента в "болшой" список может вызвать копирование имеющегося буфера. Поиск (ввиду линейной сложности, то есть полного перебора) практически бессмысленен.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, samius, Вы писали:
S>>Ну вот, лишнего перераспределения избежать удалось, но эффективное копирование может организовать лишь конструктор List<T>(IEnumerable<T>).
_FR>А что в этом копировании неэффективного?
Что именно неэффективного — это уж оффтоп. Но по сравнению с ICollection<T>.CopyTo(T[], Int32) в некоторых случаях (например в случае копирования из T[]) будет заметно сливать.
S>>На больших размерах коллекций это сыграет куда сильнее, чем лишнее перераспределение.
_FR>Что ты называешь "больших"? Будет ли при указанных размерах вообще эффективным использование List<>?
Я не имею ввиду никаких конкретных сценариев. Просто мы заговорили о том что якобы даже лишнее перераспределение массива может продиктовать наличие конструктора с capacity. И я тут пытаюсь сказать что конструктор с IEnumerable<T> возможно существует по причинам производительности. Другие способы добавления в IList<T> условно больших коллекций будут менее эффективны хоть на 10-и, хоть на 1М элементов. Они будут медленнее не идеологически, а просто в силу реализации.
Менее эффективны == займут больше времени. Но я не предлагаю устраивать бенчмарки. В большинстве сценариев меня лично устраивает метод Enumerable.ToList() и мне даже не особо любопытно, использует ли он конструктор или нет. Знаю что использует, но если бы не использовал, я бы не перестал пользоваться этим методом.
_FR>Ибо добавление одного элемента в "болшой" список может вызвать копирование имеющегося буфера. Поиск (ввиду линейной сложности, то есть полного перебора) практически бессмысленен.
После использования конструктора List<T>(IEnumerable<T>) добавление одного элемента непременно вызовет перераспределение. Но связка new List<T>(Int32) + Add(T) может оказаться медленнее.
Здравствуйте, _FRED_, Вы писали:
_FR>Допускает или нет — зависит не от типов параметров, а от того, как переданный массив используется. По одному объявлению ответить на вопрос невозможно.
Наоборот, использование в конструкторе предполагает определенное использование.
_FR>Давай с классом List<> разберёмся (поскольку его реализация нам известна), вернее с его конструктором List<>(IEnumerable<>). Я бы не добавлял такой [открытый] конструктор в класс, а конструирование списка из IEnumerable<> возложил бы на статический метод. Потому что такой вот код: _FR>
_FR>void Test(MyBusinessEntitiesCollection<MyEntity> items) {
_FR> var list = new List<MyEntity>(items);
_FR> var collection = new Collection<MyEntity>(items);
_FR>}
_FR>
_FR>мне не нравится — через конструктор не передаётся связь между аргументом и созданным объектом. В первом случае происходит копирование, а во-втором — создаётся обёртка-декоратор.
_FR>Вот так вот, имхо, было бы гораздо понятнее:
Collection принимает IList<T> а не IEnumerable, тк что твой пример не сильно удачный.
Здравствуйте, igna, Вы писали:
_FR>>ИМХО, очень сомнительный вывод. Впрочем, даже в этом топике не в первый раз, так что тролль себе дальше.
I>А зачем? Пока была вероятность, что правило Фреда имеет смысл, я пытался разобраться. А теперь все ясно. Но спасибо за разъяснения, другие просто убежали, почувствовав понятно что.
Здравствуйте, _FRED_, Вы писали:
_FR>Нет, наследование здесь не при чём. Дело просто в том, что этот конструктор не делает ничего такого, что нельзя было бы сделать без него (а конструктор с capacity делает). А в [открытый] интерфейс типа без крайней необходимости вообще лучше не добавлять ничего, чего нельзя было бы сделать без имеющегося [открытого] интерфейса.
Если IEnumerable в конструкторе контейнера и хранит аккурат то, что будет непосредтсвенно храниться в контейнере то это и есть такая необходимость.
Здравствуйте, igna, Вы писали:
F>>"и этот метод не делает ничего больше, кроме как вызов этого самого конструктора, который с теми же самыми аргументами". I>То есть все же бывают случаи, когда конструктор с указываемой Capacity интересен не только как открытый.
Это к чему?
Бывают случаи, когда конструктор с указываемой Capacity интересен как открытый. Внешний вызов требует лишнего пересоздания внутреннего буффера.
И при этом ничем тот же функционал не заменишь, кроме просто открытого метода, которая не делает ничего, помимо вызова этого конструктора. Так зачем ее городить, а не просто сделать открытый конструктор?
Здравствуйте, fmiracle, Вы писали:
F>И при этом ничем тот же функционал не заменишь, кроме просто открытого метода, которая не делает ничего, помимо вызова этого конструктора. Так зачем ее городить, а не просто сделать открытый конструктор?
Согласен. Но вряд ли это тот случай, когда "конструктор — это метод, в который передаётся некоторое начальное состояние объекта". Capacity это не состояние, а подсказка, hint.
Здравствуйте, igna, Вы писали:
I>Согласен. Но вряд ли это тот случай, когда "конструктор — это метод, в который передаётся некоторое начальное состояние объекта". Capacity это не состояние, а подсказка, hint.
Хм? Этот параметр именно определяет какое будет внутреннее состояние (размер выделенного внутреннего массива).