在项目中,特别是ui系统,经常需要做一个ui系统的管理和ui系统初始化,以及ui系统销毁等情况。
一般有两种方式来管理ui系统
1.每加一个系统就在uimanager中加一个引用。
比如(伪代码)
UIManager
{
uis.add(ASystemClass)
}
类似这样做,然后用uis来统一管理ui
优点:这样写很直观,在一个ui系统做完,可以自己管理ui系统初始化顺序
缺点:每加一个系统都需要在这里修改代码,对于开发者的维护成本比较高
2.用c#反射自动获取相关的类来管理
首先我们会有一个获取属性的基类
public class AttributeHandler<T>
{
// 消息处理表
protected Dictionary<int, T> mMsgHandlers = new Dictionary<int, T>();
protected void RegisterAttribute(Type attributeType, Assembly InAssembly)
{
ClassEnumerator Enumerator = new ClassEnumerator(
attributeType,
null,
InAssembly
);
var Iter = Enumerator.results.GetEnumerator();
while (Iter.MoveNext())
{
var ClassType = Iter.Current;
var attribute = ClassType.GetCustomAttribute(attributeType, true) as HandlerAttribute;
if (attribute != null)
{
RegisterMsgHandler(
attribute.ID,
ClassType
);
}
}
}
/// <summary>
/// 注册消息处理
/// </summary>
/// <param name="cmdID"></param>
/// <param name="handler"></param>
protected void RegisterMsgHandler(int cmdID, Type handler)
{
if (mMsgHandlers.ContainsKey(cmdID))
{
DebugHelper.LogError(string.Format("重复的事件监听, id={0}", cmdID));
return;
}
Type[] pt = new Type[0];
object[] pv = { };
//通过类型数组获取对应的类型的构造器
ConstructorInfo con = handler.GetConstructor(pt);
T conInfo = (T)(con.Invoke(pv));
//DebugHelper.Log("CmdID Add : " + cmdID);
mMsgHandlers.Add(cmdID, conInfo);
}
}
当然我们有一个ClassEnumerator是方便我们枚举出我们所有相关的类的
public class ClassEnumerator
{
protected List<Type> Results = new List<Type>();
public List<Type> results { get { return Results; } }
private Type AttributeType;
private Type InterfaceType;
public ClassEnumerator(
Type InAttributeType,
Type InInterfaceType,
Assembly InAssembly,
bool bIgnoreAbstract = true,
bool bInheritAttribute = false,
bool bShouldCrossAssembly = false
)
{
AttributeType = InAttributeType;
InterfaceType = InInterfaceType;
try
{
if (bShouldCrossAssembly)
{
Assembly[] Assemblys = AppDomain.CurrentDomain.GetAssemblies();
if (Assemblys != null)
{
for (int i = 0; i < Assemblys.Length; ++i)
{
var a = Assemblys[i];
CheckInAssembly(a, bIgnoreAbstract, bInheritAttribute);
}
}
}
else
{
CheckInAssembly(InAssembly, bIgnoreAbstract, bInheritAttribute);
}
}
catch (Exception e)
{
DebugHelper.LogError("Error in enumerate classes :" + e.Message);
}
}
protected void CheckInAssembly(
Assembly InAssembly,
bool bInIgnoreAbstract,
bool bInInheritAttribute
)
{
Type[] Types = InAssembly.GetTypes();
if (Types != null)
{
for (int i = 0; i < Types.Length; ++i)
{
var t = Types[i];
// test if it is implement from this interface
if (InterfaceType == null || InterfaceType.IsAssignableFrom(t))
{
// check if it is abstract
if (!bInIgnoreAbstract || (bInIgnoreAbstract && !t.IsAbstract))
{
// check if it have this attribute
if (t.GetCustomAttributes(AttributeType, bInInheritAttribute).Length > 0)
{
if (t.IsClass)
{
Results.Add(t);
}
// Debug.Log("Found Type:" + t.FullName + " : " + a.GetName());
}
}
}
}
}
}
}
然后我们可以有一个uimanager来初始化反射后的数据
public class SceneManager : AttributeHandler<BaseScene>
{
void IScene.Init()
{
RegisterAttribute(typeof(SceneHandlerAttribute), typeof(BaseScene).Assembly);
}
protected BaseScene ReflectionScene(ESceneName ePanelType, ref SceneHandlerAttribute attribute)
{
BaseScene basePanel = null;
mMsgHandlers.TryGetValue((int)ePanelType, out basePanel);
attribute = basePanel.GetType().GetCustomAttribute(typeof(SceneHandlerAttribute), true) as SceneHandlerAttribute;
return basePanel;
}
}
其中BaseScene就是我们的ui系统基类
我们在app初始化的勾朑调用基类的RegisterAttribute来获取所有相关的ui系统。这样就能通过反射来管理所有ui系统了。
其中ReflectionScene方法是用于获取当前的ui系统的。这其中有一个没有说的枚举是ESceneName ,这里枚举出来的是所有ui系统。
类似这样
public enum ESceneName
{
None = -1,
AppLoginScene, //登陆 scene
MainScene, //Main scene
MapScene, //地图Scene
PersonAgeScene, //个人Scene
}
然后我需要既然有需要反射的class,那么我们就需要定义我们的反射属性才行,也就是上面的SceneHandlerAttribute
public class HandlerAttribute :
Attribute,
IIdentifierAttribute<int>
{
public int mMessageID;
public int ID { get { return mMessageID; } }
public HandlerAttribute(int InMessageID)
{
mMessageID = InMessageID;
}
}
public class SceneHandlerAttribute :
HandlerAttribute,
IIdentifierAttribute<int>
{
public string mPackageURL;
public string mComponentName;
public string PackageURL { get { return mPackageURL; } }
public string ComponentName { get { return mComponentName; } }
public SceneHandlerAttribute(int InMessageID, string PackageURL, string ComponentName):base(InMessageID)
{
mMessageID = InMessageID;
mPackageURL = PackageURL;
mComponentName = ComponentName;
}
}
这样反射才知道要拿什么还有怎么从其他class重反射相关参数过管理类来。
最后就是我们真正需要用ui系统反射的地方
[SceneHandlerAttribute((int)SceneData.ESceneName.MainScene, "HomePackage", "Main")]
public class MainScene : BaseScene
{
}
类似这样,就会把该类放入uimanager中管理以及传入参数
mMessageID = SceneData.ESceneName.MainScene;
mPackageURL = “HomePackage”;
mComponentName = “Main”;
优点:我们不需要手动加代码到uimanager了,管理成本低了很多。uimanager统一管理,需要加[SceneHandlerAttribute((int)SceneData.ESceneName.MainScene, "HomePackage", "Main")]这样的头部就好了
缺点:
1.没有顺序,顺序完全是按照dll获取到的class顺序走的,所以ui系统中在初始化时不能有顺序依赖。
2.在初始化的时候因为要反射所有类,所以会有一个搜索过程,同一个工程中的类越多就搜索越久。