高级C#消息机制(Advanced CSharp Messenger)

来源:互联网 发布:积分软件哪个最好 编辑:程序博客网 时间:2024/06/14 16:26
分类:

目录(?)[+]

转载自 :http://wiki.Unity3D.com/index.PHP?title=Advanced_CSharp_Messenger


先进CSharp信使  

作者:Ilya Suzdalnitski


内容 

隐藏 ]
  • 描述
  • 前言
    • 2.1 MissingReferenceException背后的原因和解决方案
  • 的信使
    • 3.1 使用
      • 3.1.1 事件监听器
      • 3.1.2 注册一个事件侦听器
      • 3.1.3 取消注册一个事件侦听器
      • 3.1.4 广播事件
    • 3.2 清理信使
      • 3.2.1之上 永久的消息
    • 3.3 Misc
      • 3.3.1 记录所有消息
      • 3.3.2 从其他使者
    • 3.4 代码
      • 3.4.1 Callback.cs
      • 3.4.2 Messenger.cs

描述

这是一个先进的c#版本的消息传递系统。 它会自动清理事件表在一个新的水平加载。 这将防止程序员意外调用销毁方法,从而有助于防止许多MissingReferenceExceptions。 这个消息传递系统是基于杆海德 CSharpMessenger 和马格努斯Wolffelt CSharpMessenger扩展 

前言

在消息传递系统引入我们的项目( CSharpMessenger扩展 )我们开始面临非常奇怪的错误。unity3d会把MissingReferenceExceptions每次广播消息。 它会说,类,消息处理程序中声明,被毁。 问题从哪里来的,没有一个合理的解释。 然而将消息处理程序代码在try - catch块解决了这个问题。 我们明白,这不是一个好的解决方案有成百上千的try - catch块在我们的代码。 我们花了一些时间来最后找出问题在哪里。

MissingReferenceException背后的原因和解决方案

原来,MissingReferenceException错误出现了,当一个新的水平加载当前(或重新加载)。 例如,我们有一个消息 “开始游戏” 宣布如下:

public class MainMenu : MonoBehaviour {void Start (){Messenger.AddListener("start game", StartGame);} void StartGame(){Debug.Log("StartGame called in" + gameObject);  //This is the line that would throw an exception} void StartGameButtonPressed(){Messenger.Broadcast("start game");}}

乍一看,没有问题,但是水平加载后,Unity3d将抛出一个异常,称MainMenu已被摧毁。 然而没有代码,会破坏MainMenu脚本。

到底发生了什么:

  1. 我们添加了一个“开始游戏”对我们的信使消息侦听器。
  2. StartGameButtonPressed叫反过来播放“开始游戏”消息。
  3. 我们用Application.LoadLevel水平加载。
  4. 重复步骤1。
  5. 重复步骤2。

这里有eventTable使者的样子的相应的步骤:

  • 在步骤1:{ “开始游戏”,mainMenu1 - > StartGame(); }
  • 在步骤4:{ “开始游戏”,mainMenu1 - > StartGame(); } { “开始游戏”,mainMenu2 - > StartGame(); }

所以在第四步我们有两个相同的消息处理程序 “开始游戏” 消息——第一个是摧毁MainMenu对象(被破坏时重新加载一个级别),而第二种它为当前有效MainMenu对象。 事实证明,当我们广播的 “开始游戏” 消息后重新加载水平,信使调用——摧毁和有效的消息处理程序。 这就是MissingReferenceException来自的地方。

因此,显而易见——明确的解决方案 eventTable 卸货后的水平。 没有什么别的程序员是他清理桌子,它是自动完成的。

的信使

我们很高兴提供你一个高级版本的c#消息传递系统。

使用

事件监听器

void OnPropCollected( PropType propType ) {if (propType == PropType.Life)livesAmount++;}

注册一个事件侦听器

void Start() {Messenger.AddListener< Prop >( "prop collected", OnPropCollected );}

取消注册一个事件侦听器

Messenger.RemoveListener< Prop > ( "prop collected", OnPropCollected );

广播事件

public void OnTriggerEnter(Collider _collider) {Messenger.Broadcast< PropType > ( "prop collected", _collider.gameObject.GetComponent<Prop>().propType );}

清理信使

信使清理其eventTable时自动加载一个新的水平。 这将确保eventTable信使的清理,将从意想不到的MissingReferenceExceptions拯救我们。 如果你想手动清理经理eventTable,有这样一个选项通过调用Messenger.Cleanup();

永久的消息

如果你想要某个消息生存清理,马克与Messenger.MarkAsPermanent(字符串)。 这可能需要如果某个类响应消息播放来自不同的水平。


Misc

记录所有消息

用于调试目的,您可以设置 shouldLogAllMessages 国旗在信使为true。 这将记录所有调用信使。

从其他使者

快速改变旧的CSharpMessenger所有调用消息传递系统的先进,做以下步骤:

  1. 在MonoDevelop去搜索= >替换文件
  2. 在找到字段输入: 信使号<([^ < >]+)>([A-Za-z0-9_]+)。
  3. 在替换字段输入: 信使。< $ 1 > 2美元
  4. 选择范围:整体解决方案。
  5. 检查正则表达式搜索”复选框。
  6. 按更换按钮

代码

有两个文件所需的信使——工作 Callback.cs 和 Messenger.cs 

Callback.cs

public delegate void Callback();public delegate void Callback<T>(T arg1);public delegate void Callback<T, U>(T arg1, U arg2);public delegate void Callback<T, U, V>(T arg1, U arg2, V arg3);

Messenger.cs

/* * Advanced C# messenger by Ilya Suzdalnitski. V1.0 *  * Based on Rod Hyde's "CSharpMessenger" and Magnus Wolffelt's "CSharpMessenger Extended". *  * Features: * Prevents a MissingReferenceException because of a reference to a destroyed message handler. * Option to log all messages * Extensive error detection, preventing silent bugs *  * Usage examples: 1. Messenger.AddListener<GameObject>("prop collected", PropCollected);    Messenger.Broadcast<GameObject>("prop collected", prop); 2. Messenger.AddListener<float>("speed changed", SpeedChanged);    Messenger.Broadcast<float>("speed changed", 0.5f); *  * Messenger cleans up its evenTable automatically upon loading of a new level. *  * Don't forget that the messages that should survive the cleanup, should be marked with Messenger.MarkAsPermanent(string) *  */ //#define LOG_ALL_MESSAGES//#define LOG_ADD_LISTENER//#define LOG_BROADCAST_MESSAGE#define REQUIRE_LISTENER using System;using System.Collections.Generic;using UnityEngine; static internal class Messenger {#region Internal variables //Disable the unused variable warning#pragma warning disable 0414//Ensures that the MessengerHelper will be created automatically upon start of the game.static private MessengerHelper messengerHelper = ( new GameObject("MessengerHelper") ).AddComponent< MessengerHelper >();#pragma warning restore 0414 static public Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>(); //Message handlers that should never be removed, regardless of calling Cleanupstatic public List< string > permanentMessages = new List< string > ();#endregion#region Helper methods//Marks a certain message as permanent.static public void MarkAsPermanent(string eventType) {#if LOG_ALL_MESSAGESDebug.Log("Messenger MarkAsPermanent \t\"" + eventType + "\"");#endif permanentMessages.Add( eventType );}  static public void Cleanup(){#if LOG_ALL_MESSAGESDebug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed.");#endif List< string > messagesToRemove = new List<string>(); foreach (KeyValuePair<string, Delegate> pair in eventTable) {bool wasFound = false; foreach (string message in permanentMessages) {if (pair.Key == message) {wasFound = true;break;}} if (!wasFound)messagesToRemove.Add( pair.Key );} foreach (string message in messagesToRemove) {eventTable.Remove( message );}} static public void PrintEventTable(){Debug.Log("\t\t\t=== MESSENGER PrintEventTable ==="); foreach (KeyValuePair<string, Delegate> pair in eventTable) {Debug.Log("\t\t\t" + pair.Key + "\t\t" + pair.Value);} Debug.Log("\n");}#endregion #region Message logging and exception throwing    static public void OnListenerAdding(string eventType, Delegate listenerBeingAdded) {#if LOG_ALL_MESSAGES || LOG_ADD_LISTENERDebug.Log("MESSENGER OnListenerAdding \t\"" + eventType + "\"\t{" + listenerBeingAdded.Target + " -> " + listenerBeingAdded.Method + "}");#endif         if (!eventTable.ContainsKey(eventType)) {            eventTable.Add(eventType, null );        }         Delegate d = eventTable[eventType];        if (d != null && d.GetType() != listenerBeingAdded.GetType()) {            throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));        }    }     static public void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved) {#if LOG_ALL_MESSAGESDebug.Log("MESSENGER OnListenerRemoving \t\"" + eventType + "\"\t{" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}");#endif         if (eventTable.ContainsKey(eventType)) {            Delegate d = eventTable[eventType];             if (d == null) {                throw new ListenerException(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType));            } else if (d.GetType() != listenerBeingRemoved.GetType()) {                throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));            }        } else {            throw new ListenerException(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", eventType));        }    }     static public void OnListenerRemoved(string eventType) {        if (eventTable[eventType] == null) {            eventTable.Remove(eventType);        }    }     static public void OnBroadcasting(string eventType) {#if REQUIRE_LISTENER        if (!eventTable.ContainsKey(eventType)) {            throw new BroadcastException(string.Format("Broadcasting message \"{0}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.", eventType));        }#endif    }     static public BroadcastException CreateBroadcastSignatureException(string eventType) {        return new BroadcastException(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType));    }     public class BroadcastException : Exception {        public BroadcastException(string msg)            : base(msg) {        }    }     public class ListenerException : Exception {        public ListenerException(string msg)            : base(msg) {        }    }#endregion #region AddListener//No parameters    static public void AddListener(string eventType, Callback handler) {        OnListenerAdding(eventType, handler);        eventTable[eventType] = (Callback)eventTable[eventType] + handler;    } //Single parameterstatic public void AddListener<T>(string eventType, Callback<T> handler) {        OnListenerAdding(eventType, handler);        eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler;    } //Two parametersstatic public void AddListener<T, U>(string eventType, Callback<T, U> handler) {        OnListenerAdding(eventType, handler);        eventTable[eventType] = (Callback<T, U>)eventTable[eventType] + handler;    } //Three parametersstatic public void AddListener<T, U, V>(string eventType, Callback<T, U, V> handler) {        OnListenerAdding(eventType, handler);        eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] + handler;    }#endregion #region RemoveListener//No parameters    static public void RemoveListener(string eventType, Callback handler) {        OnListenerRemoving(eventType, handler);           eventTable[eventType] = (Callback)eventTable[eventType] - handler;        OnListenerRemoved(eventType);    } //Single parameterstatic public void RemoveListener<T>(string eventType, Callback<T> handler) {        OnListenerRemoving(eventType, handler);        eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler;        OnListenerRemoved(eventType);    } //Two parametersstatic public void RemoveListener<T, U>(string eventType, Callback<T, U> handler) {        OnListenerRemoving(eventType, handler);        eventTable[eventType] = (Callback<T, U>)eventTable[eventType] - handler;        OnListenerRemoved(eventType);    } //Three parametersstatic public void RemoveListener<T, U, V>(string eventType, Callback<T, U, V> handler) {        OnListenerRemoving(eventType, handler);        eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] - handler;        OnListenerRemoved(eventType);    }#endregion #region Broadcast//No parameters    static public void Broadcast(string eventType) {#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGEDebug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");#endif        OnBroadcasting(eventType);         Delegate d;        if (eventTable.TryGetValue(eventType, out d)) {            Callback callback = d as Callback;             if (callback != null) {                callback();            } else {                throw CreateBroadcastSignatureException(eventType);            }        }    } //Single parameter    static public void Broadcast<T>(string eventType, T arg1) {#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGEDebug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");#endif        OnBroadcasting(eventType);         Delegate d;        if (eventTable.TryGetValue(eventType, out d)) {            Callback<T> callback = d as Callback<T>;             if (callback != null) {                callback(arg1);            } else {                throw CreateBroadcastSignatureException(eventType);            }        }} //Two parameters    static public void Broadcast<T, U>(string eventType, T arg1, U arg2) {#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGEDebug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");#endif        OnBroadcasting(eventType);         Delegate d;        if (eventTable.TryGetValue(eventType, out d)) {            Callback<T, U> callback = d as Callback<T, U>;             if (callback != null) {                callback(arg1, arg2);            } else {                throw CreateBroadcastSignatureException(eventType);            }        }    } //Three parameters    static public void Broadcast<T, U, V>(string eventType, T arg1, U arg2, V arg3) {#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGEDebug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");#endif        OnBroadcasting(eventType);         Delegate d;        if (eventTable.TryGetValue(eventType, out d)) {            Callback<T, U, V> callback = d as Callback<T, U, V>;             if (callback != null) {                callback(arg1, arg2, arg3);            } else {                throw CreateBroadcastSignatureException(eventType);            }        }    }#endregion} //This manager will ensure that the messenger's eventTable will be cleaned up upon loading of a new level.public sealed class MessengerHelper : MonoBehaviour {void Awake (){DontDestroyOnLoad(gameObject);} //Clean up eventTable every time a new level loads.public void OnLevelWasLoaded(int unused) {Messenger.Cleanup();}}
阅读全文
0 0
原创粉丝点击