我正在使用Unity3D的GUI系统,并创建了一个围绕GUI方法的包装器(如本例中的GUI.Label())。但是,我的问题实际上是一个普遍的C#问题。
这是我的GuiLabel类,它包含了Unity GUI.Label
方法的功能,并且还订阅了单例LanguageManager.Instance
的事件。
public sealed class GuiLabel : GuiElementBase
{
#region private and protected variables
protected string m_langMgrKey;
protected GUIStyle m_style;
protected string m_text;
#endregion
public GuiLabel(Rect rect, string langMgrKey, GUIStyle style) : base(rect)
{
m_langMgrKey = langMgrKey;
m_style = style;
m_text = LanguageManager.Instance.GetTextValue(m_langMgrKey);
LanguageManager.Instance.OnChangeLanguage += HandleOnChangeLanguage;
}
void HandleOnChangeLanguage (LanguageManager thisLanguageManager)
{
m_text = LanguageManager.Instance.GetTextValue(m_langMgrKey);
}
~GuiLabel()
{
if (null != LanguageManager.Instance)
{
LanguageManager.Instance.OnChangeLanguage -= HandleOnChangeLanguage;
}
}
public override void Draw ()
{
GUI.Label(m_rect, m_text, m_style);
base.Draw ();
}
}
LanguageManager
是一个长寿单身对象,它是在LanguageManager.Instance
首次使用时创建的。 GuiElementBase
做得不多,它包含一个代表标签位置的Rect m_rect
。它不是从MonoBehavior派生出来的,它只是一个普通的类。
以下是我的问题:
1)何时取消订阅OnChangeLanguage?
我不知道如何处理来自LanguageManager的OnChangeLanguage
事件的取消订阅。我想,如果GuiLabel在语言管理器之前被破坏,那么在析构函数中执行它是否正常?
然而,如果GuiLabel和LanguageManager同时被破坏,如果仍然显示/使用GuiLabel并且用户选择退出应用程序,则会发生这种情况。在这种情况下,我什么时候应该取消订阅活动?
在GuiLabel的析构函数中访问LanguageManager.Instance是否有意义?或者这可能做得很糟糕?
2)如何在主线程上取消订阅?
第二个问题更多是针对Unity的。退出应用程序时,上面的代码会生成以下错误消息:
CompareBaseObjectsInternal只能从主线程调用。 加载场景时,将从加载线程执行构造函数和字段初始值设定项。 不要在构造函数或字段初始值设定项中使用此函数,而是将初始化代码移动到Awake或Start函数。
我理解Unity希望我在主线程上做一些事情,而不是另一个步骤。析构函数中有问题的行是null != LanguageManager.Instance
。我是否必须在主线程上比较此实例?我该怎么做?
答案 0 :(得分:0)
我在阅读When/how is IDisposable.Dispose called?后对评论中的@ j00hi实施了IDisposable的想法。来自https://forum.unity3d.com/threads/unsubscribing-from-c-events-when-gameobject-is-destroyed.224069/#post-1493692
的进一步验证public class GuiLabel : GuiElementBase, IDisposable
{
#region [ IDisposable Support ]
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// dispose managed state (managed objects).
LanguageManager.Instance.OnChangeLanguage -= HandleOnChangeLanguage;
}
disposedValue = true;
}
}
// This code added to correctly implement the disposable pattern.
void IDisposable.Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
}
#endregion [ IDisposable Support ]
}
monobehaviour应该在OnDestroy或OnDisable中调用Dispose:
private void OnDestroy()
{
((IDisposable)guiLabelInstance).Dispose(); // removes event handlers
}