gpt4 book ai didi

c# - 是否可以在C#中创建 “perfect forwarding”泛型集合?

转载 作者:搜寻专家 更新时间:2023-10-31 00:55:06 25 4
gpt4 key购买 nike

考虑以下C++代码段:

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

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

是否可以在C#中实现类似的功能?

如果是这样,它将提供真正的好处,例如在C++中,还是仅是语法糖?

编辑:这个问题专门与C#中的值类型(结构)的集合有关。我正在寻找的好处(除了简化的 Add语法之外)与C++ 11 emplace方法的好处类似-无需构造,复制和销毁(GC)临时对象。

最佳答案

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

在.Net中,结构始终被复制。任何以任何方式传递结构的尝试都将导致复制:

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

而且,这个实例实际上可能会创建至少3个实例:
  • 您创建的一个
  • 复制传递到Add方法
  • Add方法将写入的列表数据存储区中的
  • 复制

    您现在可以使用工厂删除其中的一些
    SuperList x;
    x.Add(() => new MyStruct());

    但是之后:
  • 您有一个由工厂
  • 创建的
  • ,并复制工厂将写入
  • 的列表数据存储区

    因此,如果您尝试以任何方式将新实例传递到存储中,它将被复制。

    我能想到的唯一不涉及任何副本的方法就是允许商店为您创建副本。最简单的例子是..:
    SuperList x;
    x.AddNewItems(1);

    不必介意商店如何实现它。假设它足够聪明,可以不进行任何复制。例如,它可以使用一种LinkedList <>并仅追加一个新节点。

    这样,通过要求商店为您创建一个对象,商店 有机会仅创建商品。当然,它必须正确实现,但这是另一个主题。除非您使用更多参数和一些特定逻辑详细说明 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";
    });

    最后,由于我们可以创建一个 商店并可以某种方式访问​​其项目,因此您可以执行任何将其包装在瓷层中的操作,以提供创建和初始化这些对象的任何用户前端。例如,您可以使用“constructor”方法创建一个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进行操作。随你。使用,实现或两者兼而有之,我会多次考虑采用这种方式。与 class相比, struct更加方便使用。但是,如果确实需要,所有这些当然是可能的。

    是的,问题的第二部分:

    If so, would it provide any real benefit, like in C++, or would it only be syntactic sugar?



    当然,为什么从定义上说好处是:不制作副本。这意味着,如果对象 很大,您将获得性能优势,这很明显,假设 复制花费 需要大量时间

    另一方面,如果对象很小,则由于传递了 ref指针,额外的引用,调用方法,创建委托(delegate)实例等,您将获得性能上的损失。对于小型结构,事实证明,与简单地来回复制该结构相比,您构建的基础结构仅花费更多时间(而不是空间!)来调用。进行此类优化时,应该考虑这一点。

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

    我记得许多有关值类型和引用类型的访问,GC'inc,复制等的性能测试。在Internet上进行搜索,并确保阅读它们测得的代码,因为其中有很多错误可能会使结果产生偏差。无论如何,对于复制 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[]示例中看到的那样,打破“大值(value)类型”的想法并使结构分解为引用部分太容易了。

    关于c# - 是否可以在C#中创建 “perfect forwarding”泛型集合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42993350/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com