C# 为什么这会导致InvalidOperationException:不同步?

C# 为什么这会导致InvalidOperationException:不同步?,c#,dictionary,unity3d,C#,Dictionary,Unity3d,下面代码的目标是拥有“热键”(因为没有更好的词)。这样的热键是一个切换多个复选框的复选框 我知道当你试图在循环时更改字典时,字典会抛出一个不同步的异常。但我循环的那个只不过是被人读过而已 我已经找到了一个解决方案,但我仍然对这导致异常的原因感兴趣。 public class JointTypeSelectDisplay { public Dictionary<JointType, bool> SelectedJoints; private Dictionary&l

下面代码的目标是拥有“热键”(因为没有更好的词)。这样的热键是一个切换多个复选框的复选框

我知道当你试图在循环时更改字典时,字典会抛出一个不同步的异常。但我循环的那个只不过是被人读过而已

我已经找到了一个解决方案,但我仍然对这导致异常的原因感兴趣。

public class JointTypeSelectDisplay
{
    public Dictionary<JointType, bool> SelectedJoints;  
    private Dictionary<JointType, JointTypeListElement> _jointToggles;

    private void UpdateDisplay()
    {
        foreach (var joint in SelectedJoints)
        {
            _jointToggles[joint.Key].SetState(SelectedJoints[joint.Key]);
        }
    }
}

public class JointTypeListElement
{
    public Toggle JointToggle;

    public void SetState(bool active)
    {
        JointToggle.isOn = active;
    }
}
公共类连接类型选择显示
{
公共字典选择关节;
私人词典;
私有void UpdateDisplay()
{
foreach(选定关节中的var关节)
{
_jointToggles[joint.Key].SetState(SelectedJoints[joint.Key]);
}
}
}
公共类JointTypeListElement
{
公共开关连接;
公共无效设置状态(布尔激活)
{
JointToggle.isOn=激活;
}
}
编辑:堆栈跟踪:

InvalidOperationException:不同步

System.Collections.Generic.Dictionary的2+枚举数[Windows.Kinect.JointType,System.Boolean].VerifyState()(位于/Users/builduser/buildslave/mono运行时和classlibs/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:912)

System.Collections.Generic.Dictionary的2+枚举数[Windows.Kinect.JointType,System.Boolean].MoveNext()(位于/Users/builduser/buildslave/mono运行时和classlibs/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:835)

KinectFilterTesting.JointTypeSelectDisplay.UpdateDisplay()(位于Assets/Scripts/Simon/JointTypeSelectDisplay.cs:146)

KinectFilterTesting.JointTypeSelectDisplay.SetSelectedState(System.Collections.Generic.List'1类型,布尔选择)(位于Assets/Scripts/Simon/JointTypeSelectDisplay.cs:131)

KinectFilterTesting.JointTypeSelectDisplay.SetRightArm(已选择布尔值)(位于Assets/Scripts/Simon/JointTypeSelectDisplay.cs:160)

UnityEngine.Events.InvokableCall'1[System.Boolean].Invoke(System.Object[]args)(位于C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:141)

UnityEngine.Events.InvokableCallList.Invoke(System.Object[]参数)(位于C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:574)

UnityEngine.Events.UnityEventBase.Invoke(System.Object[]参数)(位于C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:716)

UnityEngine.Events.UnityEvent'1[System.Boolean].Invoke(布尔值arg0)(位于C:/buildslave/unity/build/Runtime/Export/UnityEvent_1.cs:53)

UnityEngine.UI.Toggle.Set(布尔值,布尔sendCallback)(位于C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Toggle.cs:167)

UnityEngine.UI.Toggle.Set(布尔值)(位于C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Toggle.cs:142)

UnityEngine.UI.Toggle.set_isOn(布尔值)(位于C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Toggle.cs:136)

UnityEngine.UI.Toggle.InternalToggle()(位于C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Toggle.cs:199)

UnityEngine.UI.Toggle.OnPointerClick(UnityEngine.EventSystems.PointerEventData eventData)(位于C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Toggle.cs:210)

UnityEngine.EventSystems.ExecuteEvents.Execute(IPointerClickHandler处理程序,UnityEngine.EventSystems.BaseEventData eventData)(位于C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:52)

UnityEngine.EventSystems.ExecuteEvents.ExecuteEvents[IPointerClickHandler](UnityEngine.GameObject目标,UnityEngine.EventSystems.BaseEventData事件数据,UnityEngine.EventSystems.EventFunction'1 functor)(位于C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:269)

UnityEngine.EventSystems.EventSystem:Update()


可能是另一个线程上正在修改
selectedjoint
。解决此问题的最简单方法是复制密钥并对其进行迭代:

private void UpdateDisplay()
{
    List<JointType> joints = new List<JointType>(SelectedJoints.Keys);

    foreach (JointType joint in joints)
    {
        if(_jointToggles.ContainsKey(joint) && SelectedJoints.ContainsKey(joint))
            _jointToggles[joint].SetState(SelectedJoints[joint]);
    }
}
private void UpdateDisplay()
{
列表关节=新列表(SelectedJoints.Keys);
foreach(接头中的接头类型接头)
{
if(_jointToggles.ContainsKey(关节)和&SelectedJoints.ContainsKey(关节))
_jointToggles[joint].SetState(选定的关节[joint]);
}
}

请注意,if条件是必需的,因为错误强烈表明在执行
UpdateDisplay
时正在修改集合。此外,这也不会捕获在
UpdateDisplay
运行时将条目添加到
SelectedJoints
的情况,因此如果您还没有这样做,那么更频繁地运行
UpdateDisplay
可能是有意义的。

为什么要使用
SelectedJoints[joint.Key]
而不是
joint.Value
,一定是尝试不同解决方案后留下的。不管怎样,你的建议仍然是抛出不同步的异常。我不是建议这样可以修复错误,只是好奇而已。你能包括堆栈跟踪以便我们能准确地看到异常抛出的位置吗?@juharr我已经添加了它。这里有很多特定于unity的东西。你能提供调用
UpdateDisplay()
的上下文吗?以及如何更新
selectedjoint
?看起来,当您将一个切换设置为“开”时,它会触发另一个事件,在第一个调用仍在迭代时更改SelectedJoints的内容。感谢您的建议。正如我提到的,我已经有了一个与您的解决方案非常相似的解决方案(我循环了
SelectedJoints.Keys.ToArray()
)。至于原因,这是Kryptos在评论中建议的。也许你可以在
LateUpdate()
方法中更新状态。我认为这将防止在同一帧中触发新事件(这是我使用的一种技巧)