В вашем коде C ++ используется «конструктор заполнения» vector
, который создает все элементы первого измерения коллекции со списками, представляющими второе измерение.
Для * не существует конструктора заполнения 1036 * List<T>
объектов, что позволяет указать начальную емкость. Существующий конструктор заполнения гарантирует, что список будет иметь «достаточную» емкость для элементов, предоставленных параметром IEnumerable<T>
, но он не гарантирует, что емкость будет точно соответствовать количеству перечислимых параметров (частично потому, что перечислимые, по замыслу, не раскрывайте их мощность, поэтому единственный способ точно сопоставить емкость - изменить размер базового массива списка по одному элементу за раз.
Вы можете сделать это в две строки с небольшим Linq, три с традиционный l oop, путем создания пустого списка требуемой емкости, а затем добавления объектов второго измерения, каждый из которых инициализируется в желаемую емкость:
var myList = new List<List<T>>(5);
//Option A: Linq
myList.AddRange(Enumerable.Repeat(0, 5).Select(x => new List<string>(4)));
//Option B: Loop
for(i=0;i<5;i++)
myList.Add(new List<string>(4));
Ваш C ++, на некотором уровне, будет делать что-то похожее на одно из них, в C# просто нет ничего, чтобы абстрагировать это за конструктором.
Если разбить первый параметр, метод List<T>.AddRange()
добавляет каждый элемент IEnumerable<T>
к списку, изменяя размеры по мере необходимости (здесь не нужно, когда мы добавляем до указанной емкости). Поэтому нам нужно сгенерировать перечислимую последовательность списков. Ну, для этого есть метод Linq. Метод Enumerable.Repeat()
генерирует перечисляемую последовательность, которая повторяет указанное значение указанное количество раз. Однако если бы мы указали «new List ()» в качестве значения для повторения, этот конструктор будет выполняться только один раз (до вызова Repeat()
для оценки передаваемого значения параметра), и мы получим ту же ссылку на один List
повторяется четыре раза. Вместо этого мы хотим создать четыре списка, поэтому нам нужно определить этот конструктор как строку повторяемого кода, то есть нам нужен делегат, который мы можем выполнить как вызов метода.
Без перегрузки Enumerable.Repeat()
принимает Func<T>
и возвращает IEnumerable<T>
; если вы хотите повторить функцию с этим методом, вы получите IEnumerable<Func<T>>
. Но функция Select<T>()
может быть вызвана для перечислимой последовательности входных данных и создает перечислимую последовательность всех результатов лямбда-оператора (форма определения метода анонимного делегата), учитывая каждый элемент входных данных, перечисляемый в качестве параметра (который мы игнорируем, потому что это мусор; все, что нам нужно для перечисляемого ввода, это то, что он имеет 5 элементов). Итак, теперь у нас есть Enumerable<List<string>>
, который родительский List будет отбрасывать во внутреннюю коллекцию.
Да, я мог бы быть умнее и сделать что-то вроде:
myList.AddRange(Enumerable.Repeat(()=>new List<string>(4), 5).Select(x => x()));
Это тоже самое Дело в том, что только то, что повторяет метод Repeat()
, не является данными мусора, это ссылка на лямбду, выполняющую конструкцию объекта, которая затем вызывается в лямбде метода Select()
. Но теперь у меня есть две лямбда-выражения, которые реализованы в виде частных формульных функций класса, содержащего этот код, что добавляет к полученному объекту больше беспорядка, чем нам действительно нужно. Компилятор также не может определить тип вывода generi c для функции Repeat()
из оператора lambda, поэтому приведенный выше код не компилируется; Мне бы пришлось явно указать тип generi c, и эта строка кода (и C# как язык) достаточно многословна, как есть.