基于C#反射机制的工厂模式
简介
反射提供了描述程序集、模块和类型的对象(Type 类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了特性,可以利用反射来访问它们。
这里的类型信息包括类型的方法,变量名称,类型等信息。
基于反射机制的工厂模式
如下图所示,游戏中常用的掉落物模型,Item是基类,定义了一些基础属性,也定义了一些abstract方法。
Food和Weapon继承自Item,表示一类Item,再下一层的类就定义了具体的Item。代码如下:
Item.cs
- using UnityEngine;
- using System.Collections;
- public abstract class Item {
- protected string name;
- protected int level;
- protected int durability;
- protected int maxDurability = 10;
- protected bool isStackable;
- protected string describe;
- public string Name
- {
- get { return name; }
- set { name = value; }
- }
- public string Level
- {
- get { return name; }
- set { name = value; }
- }
- public int Durability
- {
- get { return durability; }
- set { durability = value; }
- }
- public abstract void Execute();
- public void Upgrade()
- {
- level ;
- }
- public void Fix()
- {
- durability = maxDurability;
- }
- public virtual bool IsEquipped()
- {
- return false;
- }
- }
Food.csview plain copy
- using UnityEngine;
- using System.Collections;
- using System;
- public class Food : Item {
- public Food()
- {
- isStackable = true;
- name = "Food";
- }
- public override void Execute()
- {
- Debug.Log("Eat " name);
- }
- }
Weapon.cs copy
- using UnityEngine;
- using System.Collections;
- using System;
- public class Weapon : Item
- {
- public override void Execute()
- {
- Debug.Log("Use Weapon " name);
- }
- }
FrozenCarpaccio.cs
- public class FrozenCarpaccio : Food {
- public FrozenCarpaccio()
- {
- name = "=FrozenCarpaccio";
- }
- }
Sword.cs copy
- public class Sword : Weapon {
- public Sword()
- {
- name = "Sword";
- }
- }
代码简单意思一下,具体到游戏肯定有更多的属性和方法。
现在出现的要求是根据类名来动态创建对象。
常见的方法就是一堆的switchcase....
下面用反射的方式来处理。
工厂类如下
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
- using System;
- public class ItemFactory {
- public static Dictionary<string, Type> foodClassesDict = new Dictionary<string, Type>();
- public static Dictionary<string, Type> weaponClassesict = new Dictionary<string, Type>();
- public static void CollectAllEntityClasses()
- {
- foodClassesDict.Clear();
- weaponClassesict.Clear();
- System.Reflection.Assembly[] AS = System.AppDomain.CurrentDomain.GetAssemblies();
- for (int i = 0; i < AS.Length; i )
- {
- Type[] types = AS[i].GetTypes();
- for (int j = 0; j < types.Length; j )
- {
- string className = types[j].Name;
- if (types[j].IsSubclassOf(typeof(Food)))
- {
- Debug.Log("Food" className);
- foodClassesDict.Add(className, types[j]);
- } else if (types[j].IsSubclassOf(typeof(Weapon)))
- {
- Debug.Log("Weapon" className);
- weaponClassesict.Add(className, types[j]);
- }
- }
- }
- }
- public static Food CreateFoodByClassName(string name)
- {
- Type foodType = null;
- if (foodClassesDict.TryGetValue(name, out foodType))
- {
- return Activator.CreateInstance(foodType) as Food;
- }
- else
- {
- return null;
- }
- }
- public static Weapon CreateWeaponByClassName(string name)
- {
- Type weaponType = null;
- if (weaponClassesict.TryGetValue(name, out weaponType))
- {
- return Activator.CreateInstance(weaponType) as Weapon;
- }
- else
- {
- return null;
- }
- }
- }
代码非常简单,在使用工厂之前,首先要通过反射,将类型的信息都记录到对应的Dictionary里面, 创建对象的时候只要调用对应的静态方法就可以了。
测试代码
- using UnityEngine;
- using System.Collections;
- public class ItemGenerator : MonoBehaviour {
- ItemFactory itemFactory;
- // Use this for initialization
- void Start () {
- ItemFactory.CollectAllEntityClasses();
- itemFactory = new ItemFactory();
- }
- // Update is called once per frame
- void Update () {
- if(Input.GetKeyDown(KeyCode.F5))
- {
- MysteryMeat mysteryMeat = ItemFactory.CreateFoodByClassName("MysteryMeat") as MysteryMeat;
- mysteryMeat.Execute();
- }
- if (Input.GetKeyDown(KeyCode.F6))
- {
- Dagger dagger = ItemFactory.CreateWeaponByClassName("Dagger") as Dagger;
- dagger.Execute();
- }
- }
- }
运行结果
增加可配置脚本
现在的需求是,需要用json脚本来配置一些item的属性,这样做的好处是显而易见的 - 灵活!
json的内容如下:
- [
- {
- "class": "FrozenCarpaccio",
- "name":"Frozen Carpaccio",
- "level":"1",
- "describe":"It's a piece of frozen raw meat. The only way to eat it is by cutting thin slices of it. And this way it's suprisingly good."
- },
- {
- "class": "MysteryMeat",
- "name":"the MysteryMeat",
- "level":"1",
- "describe":"Eat at your own risk!"
- },
- ]
具体的思路是在工厂中添加一个静态函数,用于加载所有class的配置属性,用json data的方式存起来。在创建对应类的时候用存好的jsondata来给对应的变量赋值。
在Factory中添加
- public static Dictionary<string, JsonData> classInfoDict = new Dictionary<string, JsonData>();
对应的方法
- public static void CollectAllItemInfo()
- {
- classInfoDict.Clear();
- TextAsset[] foodTables = Resources.LoadAll
("Data/Food"); - foreach (TextAsset table in foodTables)
- {
- string jsonStr = table.text;
- JsonData content = JsonMapper.ToObject(jsonStr);
- if (content != null)
- {
- foreach (JsonData subclass in content)
- {
- if (LitJsonUtil.JsonDataContainsKey(subclass, "class"))
- {
- string classname = subclass["class"].ToString();
- if (!classInfoDict.ContainsKey(classname))
- {
- classInfoDict.Add(classname, subclass);
- }
- }
- }
- }
- }
- }
在Item类中添加虚方法,用于初始化 copy
- public abstract void InitializeByJsonData(JsonData data);
Food类中添加实现 copy
- public override void InitializeByJsonData(JsonData data)
- {
- if (LitJsonUtil.JsonDataContainsKey(data, "name"))
- {
- name = data["name"].ToString();
- }
- if (LitJsonUtil.JsonDataContainsKey(data, "level"))
- {
- level = Int32.Parse(data["level"].ToString());
- }
- if (LitJsonUtil.JsonDataContainsKey(data, "describe"))
- {
- describe = data["describe"].ToString();
- }
- }
如果子类中有特殊的属性药初始化,可以通过override这个方法来处理。
这里还可以通过反射获取类型的变量名来自动匹配json中的key和类型的成员,然后通过SetValue方法来进行赋值。
工厂里面创建FoodItem对应的方法也要稍微改一下 copy
- public static Food CreateFoodByClassName(string className)
- {
- Type foodType = null;
- if (foodClassesDict.TryGetValue(className, out foodType))
- {
- Food tmp = Activator.CreateInstance(foodType) as Food;
- tmp.InitializeByJsonData(classInfoDict[className]);
- return tmp;
- }
- else
- {
- return null;
- }
- }
测试代码不变,可以选择把describe打印出来看看。
参考
反射(C# 和 Visual Basic) - https://msdn.microsoft.com/zh-cn/library/ms173183.aspx
动态加载和使用类型 - https://msdn.microsoft.com/zh-cn/library/k3a58006.aspx
C# 反射(Reflection)- http://www.runoob.com/csharp/csharp-reflection.html
详解C#编程中的反射机制与方法 - http://developer.51cto.com/art/200904/118971_all.htm
LitJson - https://lbv.github.io/litjson/