是否有 C++11 emplace/emplace_back 函数的 C# 模拟?

Is there any C# analogue of C++11 emplace/emplace_back functions?

从 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");

对于容器 classes (System.Collections.Generic.List),是否有任何 C++ 函数的 C# 类似物,如 emplaceemplace_back

更新: 在 C# 中,类似的代码可能会写成 list.EmplaceBack(1, "t"); 而不是 list.Add(new S(1, "t"));。最好不要记住一个 class 名字,每次在这种情况下都写 new ClassName

在 c# 中你可以使用扩展方法来实现你想要的

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");

总的来说,C# 中没有类似的东西,它的需要比 C++ 中少得多。

在 C# 中,当你有一个 List<SomeReferenceType> 时,你真正拥有的是一个 List<ReferenceToSomeType>,所以一个引用列表,每个元素的大小为 4 或 8 个字节(参见 How big is an object reference in .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 (最后只是使用放置构造函数)。

您似乎遇到了以下问题:

  1. 输入 "new S" 长。但是 "add""emplace" 短。类型由 intellisense 为您添加(只需在输入 "new " 后按 Enter):


  1. 你怕写错类型了。好吧,你不能使用 List<T>。 Intellisense 会帮你打字,无论如何编译器都不允许在编译时添加错误的类型。

  2. 性能:见.

list.Add(new S(1, "t")); 完全可以使用。

结论:我们不需要 emplace in C#.

您可以通过扩展来稍微改进
您可以使用 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));
    }
}

注意:没有检查类型和计数参数,所以如果你做错了什么,你会在运行-时间