I've written enumerators based on the existing NativeHashMap.Enumerator and UnsafeHashMap.Enumerator structs that work in burst, but I am running into trouble when I try to combine enumerators for nested unmanaged structs. Specifically I am running into a problem where the first level works, but the second level only returns the first element of the set because the enumerator it depends on is getting corrupted or reset.
我已经基于现有的NativeHashMap.Enumerator和UnSafeHashMap.Enumerator结构编写了枚举数,它们以猝发方式工作,但在尝试为嵌套的非托管结构组合枚举数时遇到了麻烦。具体地说,我遇到了一个问题,其中第一级可以工作,但第二级只返回集合中的第一个元素,因为它所依赖的枚举数已损坏或重置。
In my example I have three structs: Parent, Child, and GrandChild. Parent contains a NativeHashMap of Child, and Child contains an UnsafeHashMap of GrandChild. I can iterate over a Parent's Chilren using TestEnumerator.NativeMap. I can also iterate over a Child's GrandChildren using TestEnumerator.UnsafeMap. My problem is that when I try to combine these two enumerators into TestEnumerator.AllGrandChildren, the UnsafeMap Enumerator gets corrupted each time that AllGrandChildren.MoveNext() is called, and only the first GrandChild from every Child is returned.
在我的示例中,我有三个结构:父结构、子结构和孙子结构。父项包含子项的NativeHashMap,而子项包含孙项的UnSafeHashMap。我可以使用TestEnumerator.NativeMap迭代父母的孩子。我还可以使用TestEnumerator.UnSafeMap遍历儿童的孙子对象。我的问题是,当我尝试将这两个枚举数组合到TestEnumerator.AllGrandChildren中时,每次调用AllGrandChildren.MoveNext()时,UnSafeMap枚举器都会损坏,并且只返回每个子对象中的第一个孙子对象。
using UnityEngine;
using Unity.Collections;
using System.Collections.Generic;
using System;
using Unity.Collections.LowLevel.Unsafe;
using System.Collections;
public class NestedEnumeratorTest : MonoBehaviour
{
void Start()
{
{
TestChild child = new TestChild(Allocator.Temp);
for (int i = 0; i < 100; i++)
{
child.Add(new TestGrandChild(-1, i));
}
int count = 0;
foreach (TestGrandChild grandchild in child.GrandChildren)
{
count++;
}
Debug.Log("Number of grandchildren in child: " + count); // Logs 100
}
{
TestParent parent = new TestParent(Allocator.Temp);
for (int i = 0; i < 10; i++)
{
TestChild child = new TestChild(Allocator.Temp);
for (int j = 0; j < 10; j++)
{
child.Add(new TestGrandChild(i, j));
}
parent.Add(child);
}
int count = 0;
foreach (TestGrandChild grandchild in parent.AllGrandChildren)
{
count++;
}
Debug.Log("Number of grandchildren in parent: " + count); // Logs 10
}
}
}
public struct TestEmumerable
{
public struct AllGrandChildren : IEnumerable<TestGrandChild>
{
private NativeHashMap<int, TestChild>.Enumerator enumerator;
public readonly TestEmumerator.AllGrandChildren Enumerator => new(enumerator);
public AllGrandChildren(NativeHashMap<int, TestChild>.Enumerator enumerator)
{
this.enumerator = enumerator;
}
public IEnumerator<TestGrandChild> GetEnumerator()
{
return Enumerator;
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
public struct NativeMap<Key, Value> : IEnumerable<Value>
where Key : unmanaged, IEquatable<Key>
where Value : unmanaged
{
private NativeHashMap<Key, Value>.Enumerator enumerator;
public readonly TestEmumerator.NativeMap<Key, Value> Enumerator => new(enumerator);
public NativeMap(NativeHashMap<Key, Value>.Enumerator enumerator)
{
this.enumerator = enumerator;
}
public IEnumerator<Value> GetEnumerator()
{
return Enumerator;
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
public struct UnsafeMap<Key, Value> : IEnumerable<Value>
where Key : unmanaged, IEquatable<Key>
where Value : unmanaged
{
private UnsafeHashMap<Key, Value>.Enumerator enumerator;
public readonly TestEmumerator.UnsafeMap<Key, Value> Enumerator => new(enumerator);
public UnsafeMap(UnsafeHashMap<Key, Value>.Enumerator enumerator)
{
this.enumerator = enumerator;
}
public IEnumerator<Value> GetEnumerator()
{
return Enumerator;
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
}
public struct TestEmumerator
{
public struct AllGrandChildren : IEnumerator<TestGrandChild>
{
private NativeMap<int, TestChild> children; // Why can't I use a custom type here?
private UnsafeMap<int, TestGrandChild> grandchildren;
private NativeReference<bool> started;
public AllGrandChildren(NativeHashMap<int, TestChild>.Enumerator children)
{
this.children = new(children);
grandchildren = new();
started = new(false, Allocator.Temp);
}
public TestGrandChild Current => grandchildren.Current;
object IEnumerator.Current => Current;
public bool MoveNext()
{
bool moveNext;
if (started.Value)
{
moveNext = grandchildren.MoveNext();
}
else
{
started.Value = true;
moveNext = false;
}
while (!moveNext && children.MoveNext())
{
grandchildren = children.Current.GrandChildren.Enumerator;
moveNext = grandchildren.MoveNext();
}
return moveNext;
}
public void Reset()
{
if (started.Value)
{
started.Value = false;
}
children.Reset();
}
public void Dispose()
{
}
}
public struct NativeMap<Key, Value> : IEnumerator<Value>, IDisposable
where Key : unmanaged, IEquatable<Key>
where Value : unmanaged
{
private NativeHashMap<Key, Value>.Enumerator enumerator;
public NativeMap(NativeHashMap<Key, Value>.Enumerator enumerator)
{
this.enumerator = enumerator;
}
public Value Current => enumerator.Current.Value;
object IEnumerator.Current => Current;
public void Dispose()
{
}
public bool MoveNext()
{
return enumerator.MoveNext();
}
public void Reset()
{
enumerator.Reset();
}
}
public struct UnsafeMap<Key, Value> : IEnumerator<Value>, IDisposable
where Key : unmanaged, IEquatable<Key>
where Value : unmanaged
{
private UnsafeHashMap<Key, Value>.Enumerator enumerator;
public UnsafeMap(UnsafeHashMap<Key, Value>.Enumerator enumerator)
{
this.enumerator = enumerator;
}
public unsafe Value Current => enumerator.Current.Value;
object IEnumerator.Current => Current;
public void Dispose()
{
}
public bool MoveNext()
{
return enumerator.MoveNext();
}
public void Reset()
{
enumerator.Reset();
}
}
}
public struct TestParent
{
NativeHashMap<int, TestChild> children;
public TestEmumerable.NativeMap<int, TestChild> Children => new(children.GetEnumerator());
public TestEmumerable.AllGrandChildren AllGrandChildren => new(children.GetEnumerator());
public TestParent(Allocator allocator)
{
children = new(10, allocator);
}
public void Add(TestChild child)
{
children[children.Count] = child;
}
}
public struct TestChild
{
private UnsafeHashMap<int, TestGrandChild> grandchildren;
public TestEmumerable.UnsafeMap<int, TestGrandChild> GrandChildren => new(grandchildren.GetEnumerator());
public TestChild(Allocator allocator)
{
grandchildren = new(10, allocator);
}
public void Add(TestGrandChild grandchild)
{
grandchildren[grandchildren.Count] = grandchild;
}
}
public struct TestGrandChild
{
int parent;
int child;
public TestGrandChild(int parent, int child)
{
this.parent = parent;
this.child = child;
}
}
Edit:
I solved my problem, but I don't know why my solution works. In TestEmumerator.AllGrandChildren, If I use NativeHashMap.Enumerator
instead of my custom TestEnumerator.NativeMap
type for the children
property, everything starts working. Can someone help explain why this is?
编辑:我解决了我的问题,但我不知道我的解决方案为什么有效。在TestEmumerator.AllGrandChildren中,如果我使用NativeHashMap.Enumerator而不是我的定制TestEnumerator.NativeMap类型作为Child属性,则一切都将开始工作。有人能解释一下为什么会这样吗?
更多回答
我是一名优秀的程序员,十分优秀!