是否有可能创造一个完美的转发" C#中的泛型集合?

时间:2017-03-24 07:02:23

标签: c# c++ c++11

考虑这个C ++代码片段:

std::vector<Foo> bar;
bar.emplace_back(9001);    //Foo defines a constructor that takes int

这将创建一个Foo类型的对象,传递任何构造函数参数,并将新对象存储在向量中。在功能上等同于bar.push_back(Foo(9001));,但效率更高,因为没有创建临时Foo

有没有办法在C#中实现类似的功能?

如果是这样,它会提供任何真正的好处,比如在C ++中,还是只有语法糖

编辑:问题是关于C#(结构)中的值类型集合;我正在寻找的好处(除了简化的Add语法)类似于C ++ 11 emplace方法 - 不需要构造,复制和销毁(GC)临时对象。

1 个答案:

答案 0 :(得分:3)

是的,这是可能的,但它很麻烦。

在.Net中,结构总是复制。任何以任何方式传递结构的尝试都将产生一个副本:

SuperList x;
x.Add(new MyStruct());

更重要的是,这个实际上可能会创建至少3个实例:

  • 您创建的
  • 复制传递给Add方法
  • 复制Add方法将写入的列表数据存储区

您现在可以使用工厂

删除其中一些内容
SuperList x;
x.Add(() => new MyStruct());

然后:

  • 你有一个由工厂创建的
  • 并复制工厂将写入的列表数据存储区

所以..如果你试图以任何方式将新实例传递到商店,它将被复制。

我能想到的唯一方法就是不允许制作任何副本是为了让商店为您创建。最简单的例子是......:

SuperList x;
x.AddNewItems(1);

没关系商店如何实现它。让我们说它很聪明,不做任何副本。例如,它可以使用一种LinkedList&lt;&gt;然后添加一个新节点。

这样,通过要求商店为您创建对象,商店有机会只创建项目。当然,它必须正确实施,但这是另一个主题。除非您使用更多参数和一些特定逻辑详细说明CreateItem,否则可能会使用所有默认值创建新项目,但是..让我们说使用默认值创建它是正常的。

现在怎么办?当然,现在有一个问题是工作(即阅读,修改等)项目:

class SuperList
{
    MyStruct GetItem(int id);
}

嗯,这显然会返回一份副本。糟糕。

如果你使用的是C#7.0,你可以使用ref变量,也许,我还没有尝试过:

class SuperList
{
    ref MyStruct GetItem(int idx);
}

ref MyStruct aRef = x.GetItem(x.Count-1);
aRef.Name = "mom";

但除非它是C#7,否则你不能也不得不长期工作:

class SuperList
{
    delegate void ItemAccessor(ref MyStruct y); // <- REF! no copy
    void WorkWithItem(int idx, ItemAccessor func);
}

x.WorkWithItem(1, (ref MyStruct it) => {
    it.Name="mom";
});

最后,由于我们可以创建商店并且可以以某种方式访问​​其项目,您可以执行任何您想要将其包装在瓷砖图层中的任何内容,以提供创建和初始化这些对象的任何用户前端。例如,您可以使用“构造函数”方法创建API:

var x = new SuperList<T,U,...>( (ref item, T t, U u, ..) => {
    item.First = t;
    item.Second = u;
    ...
});

x.CreateItem(new T(), new U(), ...);

或通过object[] args和Reflection进行。随你。使用,实现或两者都很麻烦,我会多次想到这样做。使用classstruct更方便。但如果确实需要的话,所有这一切都是可能的。

啊,是的,问题的第二部分:

  

如果是这样,它会提供任何真正的好处,比如C ++,还是仅仅是语法糖?

当然,为什么定义的好处是:不制作副本。这意味着,如果对象很大,您将获得性能优势,显而易见的是,假设复制需要重要时间

另一方面,如果对象很小,则会因为传递ref指针,额外的deferences,调用方法,创建委托实例等而受到性能影响。对于小型结构,可能会发现您构建的基础结构只需要花费更多时间(而不是空间!)来调用,而不是简单地反复复制该结构。在针对此类优化时,您应该考虑这一点。

我实际上不知道要获得收益需要多大的结构。复制结构很快,它们被设计为它。它实际上几乎是原始的逐字节副本,处理器可以非常快

我记得很多关于值类型和引用类型的访问,GC'inc,复制等的性能测试。在互联网上搜索,并确保阅读他们测量的代码,因为有很多错误可能会扭曲结果。无论如何,对于复制structs来说,这都是关于它们的字节大小和复制内存块的速度。

所以..拥有一个庞大的结构..让我们的SmartList值得。什么放在结构中?大量的数据?不,结构会很小,它会保留对大数组的引用:

struct X { public int[] f; }
static void Main()
{
    X a;a.f = new []{5};
    X b;b = a;
    a.f[0]=4;
    Console.WriteLine("Hello " + b.f[0]); // FOUR!
}

如果int[]中的struct实际上是一个引用,那么保留在结构中的几乎任何通常都是一个占用空间很小的小引用。您需要struct 字段数量和/或大部分字段需要是重度非数组值类型他们自己..这不是你经常看到的东西。正如你在int[]示例中所看到的那样,打破“大价值类型”的想法并将结构分解为引用的部分太容易了。