【问题标题】:How to move two enumerators simultaneously?如何同时移动两个枚举器?
【发布时间】:2019-05-19 06:30:13
【问题描述】:

我有两个包含两个只读结构的大数组:A 和 B。我需要枚举它们,但我需要以最优化的方式进行。

readonly struct A {
   long a;
   long b;
   long c;
   long d;
   long e;
   long g;
}

readonly struct B {
   byte a;
   ushort b;
}

如您所见,类型 A 非常大 struct 以一遍又一遍地复制它,所以我创建了我的自定义枚举器,它通过引用 (ref return) 返回此 struct。 B 型不需要它,因为它的大小只有 4 个字节。现在我需要让两个枚举器同时移动,只使用一个 foreach 循环,因为直接调用枚举器的方法(即 MoveNext、Current)等等看起来并不多。

我的问题是,当我尝试将两个结构保存在一个聚合中时,struct A 被复制并且我失去了性能增益。

public readonly struct Aggregate
{
    public readonly A ItemA;
    public readonly B ItemB;

    public Aggregate(A itemA, in B itemB)
    {
        ItemA = itemA;
        ItemB = itemB;
    }
}

此聚合的总大小为 56 字节。当我将它分配给另一个结构的属性时,C# 复制该结构听起来很合乎逻辑。但是我能用它做什么呢?我只需要对数组元素的引用。我认为使用不安全代码获取指针不是正确的方法,因为 GC 可以移动我的数组(如果它足够小并且不在 LOH 区域中)。

那么您可以向我提出什么解决方案?

【问题讨论】:

  • 我看不到不使用 IEnumerable 的原因
  • @Steve,对我来说一遍又一遍地复制结构并不是很好。我在问题中写了它
  • 你没有告诉我们你在哪里复制它们。如果您只需要枚举两个数组,那么您什么都不会复制。如果你想通过 ref addr 引用,你应该使用 class 而不是 struct
  • 使A 成为一个类,而不是一个结构。这就是您在使用它的任何地方获得引用语义的方式,这似乎是您想要的。
  • @Servy,那么我的程序会受到影响,因为 GC 会关心堆中的所有对象

标签: c# .net struct ref


【解决方案1】:

我不知道我是否理解你的问题是否正确,但你在谈论数组。

假设:两者都是大小相同的数组

你可以这样做

for (int i = 0; i < length; i++)
{
    ref var aItem = ref arrayA[i];
    ref var bItem = ref arrayB[i];
    //do your stuff
}

【讨论】:

  • 我喜欢这个解决方案,因为它不需要自定义枚举器。
  • 这样的数组有很多,Enumerator 允许我对客户端全部隐藏
  • @Dobby007,但如果客户不知道您的特殊_current 并返回 ref,他将不会使用它。
  • @OlivierJacot-Descombes,整个逻辑被封装在一个地方,所以到目前为止我可以在许多其他地方重用它
  • @Dobby007 你能否提供一些额外的代码(例如你的客户),以便我们更好地了解
【解决方案2】:

您可以显式使用枚举器,而无需 foreach 循环:

var aEnumerator = listA.GetEnumerator();
var bEnumerator = listB.GetEnumerator();
while (aEnumerator.MoveNext() && bEnumerator.MoveNext()) {
    var aItem = aEnumerator.Current;
    var bItem = bEnumerator.Current;
    //TODO: do some work
}

【讨论】:

  • 是的,我知道我可以做到,但我写道:“现在我需要让两个枚举器同时移动,只使用一个 foreach 循环,因为直接调用枚举器的方法看起来并不多(即MoveNext、Current) 等等。”
  • 这是我知道的唯一方法,你可以同时枚举。但是我不确定您将如何使用参考返回。您是否添加了另一个属性,例如 public ref int RefCurrent { get { return ref _current; } } ?因为IEnumerator 不允许 ref 在其常规Current 属性上返回。
  • 是的,完全正确。我添加了私有属性 _current 并使用 ref return 通过引用返回它。鸭子打字帮助了我。
猜你喜欢
  • 2013-04-25
  • 2012-11-17
  • 2015-08-22
  • 2010-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-17
相关资源
最近更新 更多