Есть ли в C # аналог функций emplace / emplace_back в C ++ 11?

Начиная с C ++ 11, можно написать что-то вроде

#include <vector>
#include <string>

struct S
{

    S(int x, const std::string& s)
        : x(x)
        , s(s)
    {
    }

    int x;
    std::string s;

};

// ...

std::vector<S> v;

// add new object to the vector v
// only parameters of added object's constructor are passed to the function
v.emplace_back(1, "t");

Есть ли в C # аналог функций C ++, например emplace или emplace_back для контейнерных классов (System.Collections.Generic.List)?

Обновление. В C # аналогичный код может быть записан как list.EmplaceBack(1, "t"); вместо list.Add(new S(1, "t"));. Было бы неплохо не запоминать имя класса и каждый раз писать new ClassName в таких ситуациях.


person Constructor    schedule 14.03.2016    source источник
comment
@PatrickHofman: 2 вещи. Он создает объект непосредственно на месте в предполагаемом месте в векторе (в отличие от копирования или перемещения уже существующего объекта) и пересылает аргументы непосредственно в конструктор объекта.   -  person Benjamin Lindley    schedule 14.03.2016
comment
Разве все объекты C # не всегда выделяются динамически, в основном всегда с указателем? Что принесет такая функциональность?   -  person Angew is no longer proud of SO    schedule 14.03.2016
comment
@Angew Для типов данных struct это будет немного быстрее. Для больших struct типов данных это, вероятно, будет быстрее. Но struct типы данных не должны быть большими, так что это было бы решением для поиска проблемы :-)   -  person xanatos    schedule 14.03.2016
comment
на самом деле для списка это то же самое, что myList.Add (new S (1, t)); edit: from cppReference Добавляет новый элемент в конец контейнера. Элемент создается с помощью std :: allocator_traits :: construct, который обычно использует новое размещение для создания элемента на месте в месте, указанном контейнером. Аргументы args ... передаются конструктору как std :: forward ‹Args› (args) ....   -  person Boo    schedule 14.03.2016
comment
@Angew Для уменьшения размера кода. С emplace вам не нужно писать v.insert(S(1, "t")); на C ++. В C # это может быть написано как list.EmplaceBack(1, "t"); вместо list.Add(new S(1, "t"));.   -  person Constructor    schedule 14.03.2016
comment
@Boo С гипотетическим EmplaceBack вам не нужно будет писать new ClassName каждый раз.   -  person Constructor    schedule 14.03.2016
comment
Было бы лучше, если бы вы написали C# реализацию и указали, что с ней не так.   -  person Sinatr    schedule 14.03.2016
comment
@Boo 1) Имена классов обычно достаточно длинные. 2) Вам всегда нужно помнить об этом, когда вы пишете конструкцию с функцией Add. 3) Для такой функции можно выбрать более короткое имя.   -  person Constructor    schedule 14.03.2016
comment
@Sinatr Вопрос обновлен.   -  person Constructor    schedule 14.03.2016
comment
Если вы считаете, что это ценная функция для C #, вы всегда можете добавить запрос функции на connect.microsoft.com . Однако я был бы очень, очень удивлен, если бы он попал в окончательный список реализованных функций. Способ сделать это уже есть, очень немногие жаловались на необходимость вводить имя класса за 17 лет существования C #, и он не служит практической цели в C # (в отличие от C ++). Плюс объем проектирования, тестирования и документации, вероятно, намного перевешивает предполагаемую выгоду. Привыкайте набирать имена классов ...   -  person D Stanley    schedule 14.03.2016


Ответы (4)


Вы можете немного улучшить вариант @Boo с помощью расширения.
Вы можете создать экземпляр объекта с помощью Activator.CreateInstance, чтобы сделать решение более универсальным.

public static class ListExtension
{
    public static void Emplace<S>(this IList<S> list, params object[] parameters)
    {
        list.Add((S)Activator.CreateInstance(typeof(S), parameters));
    }
}

Примечание: не проверяются параметры типа и подсчета, поэтому, если вы сделаете что-то не так, вы получите ошибки только во время выполнения

person Grundy    schedule 14.03.2016

В целом в C # ничего подобного нет, да и потребность в нем намного меньше, чем в C ++.

В C #, когда у вас есть List<SomeReferenceType>, на самом деле у вас есть List<ReferenceToSomeType>, поэтому список ссылок с размером каждого элемента 4 или 8 байтов (см. Насколько велика ссылка на объект в .NET?). Копирование ссылки не приводит к дублированию базового объекта, поэтому оно выполняется очень быстро (вы копируете около 4 или 8 байтов, и процессор оптимизирован для этой операции, потому что это размер собственного указателя процессора. ). Итак, когда вы someList.Add(someReference) делаете то, что делаете, это добавляете ссылку на ваш List<>.

В C ++, когда у вас есть std::vector<SomeType>, у вас есть вектор SomeType с размером каждого элемента, равным sizeof(SomeType). Вставка нового элемента в std::vector<> вызовет дублирование вставляемого вами элемента (клонирование, копирование ... выберите глагол, который вам нравится). Это дорогостоящая операция.

Довольно часто шаблон, который вы используете, состоит в том, что вы создаете объект просто для того, чтобы вставить его в std::vector<>. Чтобы оптимизировать эту операцию в C ++ 11, они добавили два способа сделать это: метод std::vector<>::emplace и поддержку std::vector<> семантики перемещения. Разница в том, что семантика перемещения должна поддерживаться типом SomeType (вам нужен конструктор перемещения со спецификатором noexcept), в то время как каждый тип поддерживает emplace (который в итоге просто использовал конструктор размещения).

person xanatos    schedule 14.03.2016
comment
Необходимо уменьшить размер кода: с гипотетическим EmplaceBack вам не нужно каждый раз писать new ClassName в Add выражении. - person Constructor; 14.03.2016
comment
@Constructor: если бы все, что он делал в C ++, избавляло вас от небольшого набора текста, его бы не существовало, потому что это определенно не является одной из мотиваций для этого. - person Benjamin Lindley; 14.03.2016
comment
@BenjaminLindley Конечно, ты прав. Но как хорошо не писать каждый раз ClassName (C ++) или new ClassName (C #) в таких ситуациях. - person Constructor; 14.03.2016
comment
@Constructor Есть по крайней мере еще одна причина, по которой в C # это было бы менее полезно ... В C ++, если вы попытаетесь поместить объект DerivedType внутри std::vector<BaseType> случаются очень плохие вещи, и никому не нужны эти плохие вещи. В C # вполне нормально помещать ссылку на DerivedType в BaseType переменную / контейнер (и это общий шаблон). Так что иметь в List<BaseType> Emplace(), который может строить только BaseType, было бы менее полезно. - person xanatos; 14.03.2016
comment
@xanatos Да, ты прав. Но по крайней мере в 95% случаев вы добавляете в список объекты того же типа, который вы указали при объявлении списка, поэтому такая функция может быть достаточно полезной. - person Constructor; 14.03.2016
comment
@Constructor Не все шаблоны, которые могут быть полезны, реализованы в C #. Это потребует поддержки параметризованных ограничений new() для универсальных, плюс вариативные универсальные шаблоны (плюс некоторая связь между ними). C ++ намного более продвинут в метапрограммировании времени компиляции, чем C #. - person xanatos; 14.03.2016
comment
@xanatos Да, я начал понимать эту печальную вещь. Есть ли какой-нибудь язык .NET с такими мощными возможностями метапрограммирования, как у C ++? - person Constructor; 14.03.2016
comment
@Constructor Я действительно чувствую, что вы меняете побочный эффект (нет необходимости явно указывать имя класса) по реальной причине (без копирования во время вставки). C ++ намного более подробен, чем C #. Вся его мощь метапрограммирования часто высовывает свою уродливую голову, и тогда у вас появляются строки кода, полные std::move, std::forward и других эзотерических вещей. - person xanatos; 14.03.2016
comment
@xanatos Этот побочный эффект делает программный код намного лучше. Для меня лучше иметь некрасивый способ что-то делать, чем не иметь его. IMHO, метапрограммирование C ++ уродливо, но ограничения языка C # в этом случае тоже уродливы. Я хочу избежать дублирования кода во всех его формах. - person Constructor; 14.03.2016
comment
Да, но что, если у вас есть Список ‹struct›? Еще бы пригодилось! - person user643011; 01.04.2020

в С # вы можете использовать метод расширения для достижения того, что хотите

public static class ListExtension
{
    public static void Emplace(this IList<S> list, int x, string s)
    {
        list.Add(new S(x, s));
    }
}

тогда используйте это так

myList.Emplace(1,"t");
person Boo    schedule 14.03.2016
comment
И написать такой метод для всех перегрузок всех конструкторов всех классов, которые я хочу быть членами коллекций? Я слишком ленив... - person Constructor; 14.03.2016
comment
@Constructor, вы можете создать экземпляр объекта с отражением, чтобы сделать решение более универсальным - person Grundy; 14.03.2016
comment
@Grundy Как это возможно? - person Constructor; 14.03.2016
comment
@Constructor, что-то вроде public static void Emplace<S>(this IList<S> list, params object[] parameters){ list.Add((S)Activator.CreateInstance(typeof(S), parameters))} - person Grundy; 14.03.2016
comment
@Grund Спасибо. Не могли бы вы добавить свой комментарий в качестве ответа? - person Constructor; 14.03.2016

Похоже, у вас следующие проблемы:

  1. Это длиннее для ввода на "new S". Но "add" короче "emplace". Тип добавлен intellisense (просто нажмите Enter после ввода "new "):


  1. Вы боитесь написать неправильный шрифт. Ну ты не можешь с List<T>. Intellisense поможет вам набирать текст, а компилятор все равно не позволит добавить неправильный тип во время компиляции.

  2. Производительность: см. ответ @Xanatos.

list.Add(new S(1, "t")); идеально подходит для использования.

Вывод: нам не нужно emplace в C#.

person Sinatr    schedule 14.03.2016
comment
Простой вопрос: зачем мне набирать все, что можно добавить с помощью интеллекта (программу, а не человека)? Задача программиста - делать вещи, которые можно автоматизировать на уровне IDE / компилятора. - person Constructor; 14.03.2016
comment
Я не понимаю вашего комментария. Не могли бы вы привести пример, поясняющий, что вы имеете в виду? Intellisense - это не человек, поэтому он может предложить помощь только в определенных точках (например, при вводе . отобразится список доступных элементов того, что находится слева). Возможно, удастся автоматизировать emplace (используя препроцессор, например расширение VS, не уверен здесь), но я не вижу ни единого верного аргумента, зачем мне это нужно. - person Sinatr; 14.03.2016
comment
Вы писали: Типа вам добавляет intellisense. Если intellisense достаточно умен, чтобы добавить тип, зачем мне делать это самому? Это можно просто сделать на уровне компилятора. - person Constructor; 14.03.2016
comment
Теперь я понимаю вашу точку зрения. Я бы предпочел add + new себя, даже если бы была emplace механика. Постоянно обращаются к неправильным / скучным вещам (см. C# 6.0). Попадет ли когда-нибудь в список та самая незначительная вещь, которую вы хотите? Я сомневаюсь. - person Sinatr; 14.03.2016