反射
定義
- 類型 Type:描述程序集棍丐,類型等的元數(shù)據(jù)信息
- 反射 Reflect:
一個運行時獲取元數(shù)據(jù)信息的過程,得到一個給定程序集所包含的所有類型的列表丽蝎,包括給定類型的方法斗塘,字段,屬性和事件擎鸠,接口,方法的參數(shù)和其他相關(guān)細節(jié)(基類缘圈,命名空間劣光,清單數(shù)據(jù))
System.Type 類
- 常用屬性 IsClass IsEnum IsAbstrace IsValueType IsInterface
- 常用方法 GetFields GetInterface GetMethods GetMembers
- 獲取實例 GetType
獲取Type
- Object.GetType()
TestObject o = new TestObject();
Type t = o.GetType();
- typeof()
Type t = typeof(o);
- System.Type.GetType()
Type t = Type.GetType("TestObject");
查看元數(shù)據(jù)
class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
ListMethods2(args.GetType());
ListMethods(args.GetType());
ListFields(args.GetType());
ListProps(args.GetType());
ListInterface(args.GetType());
ListVariousStats(args.GetType());
}
/// <summary>
/// 反射方法 參數(shù) 和 返回值
/// </summary>
/// <param name="t"></param>
static void ListMethods(Type t)
{
Console.WriteLine("*****Mthods****");
MethodInfo[] mi = t.GetMethods();
foreach (MethodInfo m in mi)
{
//得到返回類型
string retVal = m.ReturnType.FullName;
string paramInfo = "( ";
//得到參數(shù)
foreach(ParameterInfo pi in m.GetParameters())
{
paramInfo += string.Format("{0} {1}", pi.ParameterType, pi.Name);
}
paramInfo += " )";
Console.WriteLine("->{0} {1} {2}", retVal, m.Name, paramInfo);
}
Console.WriteLine();
}
/// <summary>
/// getmethod toString()
/// </summary>
/// <param name="t"></param>
static void ListMethods2(Type t)
{
Console.WriteLine("*****Methods2******");
var methodName = from n in t.GetMethods() select n;
foreach (var name in methodName)
Console.WriteLine("->{0}", name);
Console.WriteLine();
}
/// <summary>
/// 反射字段
/// </summary>
/// <param name="t"></param>
static void ListFields(Type t)
{
Console.WriteLine("*****Fields****");
var fileldName = from f in t.GetFields() select f.Name;
foreach (var name in fileldName)
Console.WriteLine("->{0}", name);
Console.WriteLine();
}
/// <summary>
/// 反射屬性
/// </summary>
/// <param name="t"></param>
static void ListProps(Type t)
{
Console.WriteLine("*****Props****");
var propName = from f in t.GetProperties() select f.Name;
foreach (var name in propName)
Console.WriteLine("->{0}", name);
Console.WriteLine();
}
/// <summary>
/// 反射接口
/// </summary>
/// <param name="t"></param>
static void ListInterface(Type t)
{
Console.WriteLine("*****Interface****");
var propName = from f in t.GetInterfaces() select f.Name;
foreach (var name in propName)
Console.WriteLine("->{0}", name);
Console.WriteLine();
}
/// <summary>
/// 顯示其他信息
/// </summary>
/// <param name="t"></param>
static void ListVariousStats(Type t)
{
Console.WriteLine("*****Various stats****");
Console.WriteLine("Is type abstract? {0}", t.IsAbstract);
Console.WriteLine("Is type sealed? {0}", t.IsSealed);
Console.WriteLine("Is type IsGenericTypeDefinition? {0}", t.IsGenericTypeDefinition);
Console.WriteLine("Is type IsClass? {0}", t.IsClass);
}
}
反射程序集
Assembly.Load 動態(tài)加載程序集,反射獲取程序集的元數(shù)據(jù)
class MainClass
{
// 輸入 文件名糟把,不需要帶.dll 后綴
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.WriteLine("***** External Assembly Viewer ****");
string asmName = "";
Assembly asm = null;
do
{
Console.WriteLine("\n Enter as assembly to evaluate");
Console.WriteLine("or enter Q to quit");
asmName = Console.ReadLine();
if (asmName.ToUpper() == "Q")
{
break;
}
try
{
asm = Assembly.Load(asmName);
DisplayTypesInAsm(asm);
}
catch
{
Console.WriteLine("sorry, cant find assembly");
}
}
while (true);
}
static void DisplayTypesInAsm(Assembly asm)
{
Console.WriteLine("\n**** Types in Assembly ****");
Console.WriteLine("->{0}", asm.FullName);
Type[] types = asm.GetTypes();
foreach (Type t in types)
Console.WriteLine("Type: {0}", t);
Console.WriteLine("");
}
}
晚期綁定(late binding)
定義
創(chuàng)建一個給定類型的實例绢涡,并在運行時調(diào)用其方法,而不需要在編譯期知道它存在的一種技術(shù)
方法
System.Activator.CreateInstance() 創(chuàng)建運行時實例
MethodInfo.Invoke() 執(zhí)行實例的方法
class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Assembly a = null;
try
{
a = Assembly.Load("hellodll2");
}
catch(Exception e)
{
Console.WriteLine(e);
return;
}
if (a != null)
{
CreateUsingLateBinding(a);
}
Console.ReadLine();
}
static void CreateUsingLateBinding(Assembly asm)
{
try
{
Type myClass = asm.GetType("hellodll2.MyClass");
object obj = Activator.CreateInstance(myClass);
Console.WriteLine("Create a {0} using late binding!", obj);
//調(diào)用無參數(shù)函數(shù)
MethodInfo hello = myClass.GetMethod("hello");
Console.WriteLine("call {0} ", hello.Invoke(obj, null));
//調(diào)用包含參數(shù)的函數(shù)
MethodInfo show = myClass.GetMethod("show");
Console.WriteLine("call {0} ", show.Invoke(obj, new object[] { "liwen", 35}));
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
namespace hellodll2
{
public class MyClass
{
public MyClass()
{
}
public static string hello()
{
return "hello world";
}
public static string show(string name, int age)
{
return string.Format("name:{0} age: {1}", name, age);
}
}
}
特性
C#常用特性
- [Obsolete]
- [Serializable]
- [NonSerialized]
自定義特性
/// <summary>
/// 自定義特性 AttributeTargets:限定應(yīng)用范圍遣疯; Inherited是否能夠給派生類集成
/// </summary>
[AttributeUsage( AttributeTargets.Field, Inherited =false)]
public sealed class VehicleDescriptionAttribute : System.Attribute
{
public string Description { get; set; }
public VehicleDescriptionAttribute(string description)
{
Description = description;
}
public VehicleDescriptionAttribute() { }
}
使用特性
[Serializable]
public class Motorcycle
{
[NonSerialized]
float weightOfCurrentPassengers;
[Obsolete("use another vehicle")]
public bool hasRadioSystem;
[VehicleDescription("head set")]
public bool hasHeadSet;
bool hasSissyBar;
}
反射獲取特性
class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Motorcycle motorcycle = new Motorcycle();
motorcycle.hasRadioSystem = true;
motorcycle.hasHeadSet = true;
ReflectOnAttributeUsageEarlyBinding();
}
/// <summary>
/// 使用早綁定 反射特性
/// </summary>
private static void ReflectOnAttributeUsageEarlyBinding()
{
Type t = typeof(Motorcycle);
object[] customAttr = t.GetCustomAttributes(false);
foreach (var v in customAttr)
{
if (v is VehicleDescriptionAttribute)
{
Console.WriteLine("-->{0}\n", ((VehicleDescriptionAttribute)v).Description);
}
}
}
}
Unity TNet 反射和特性的應(yīng)用
- [RFC] 定義遠程函數(shù)調(diào)用特性
- CachedFunc 封裝MethodInfo.Invoke函數(shù)執(zhí)行方法
- BuildMethodList 發(fā)射獲取RFC 函數(shù)并保存到本地列表中
- 從本地列表中查詢RFC函數(shù)執(zhí)行
/// <summary>
/// TNet 反射 特性應(yīng)用
/// 用RFC 特性
/// 利用反射機制 保存具有RFC的obj 和 函數(shù)
/// 然后網(wǎng)絡(luò)層收到消息后執(zhí)行函數(shù)
/// </summary>
namespace Lesson6
{
class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
MainClass ml = new MainClass();
BuildMethodList(ml);
CachedFunc ent = null;
if (mDict1 != null) mDict1.TryGetValue("testRFC", out ent);
ent.Execute();
if (mDict1 != null) mDict1.TryGetValue("testRFC2", out ent);
ent.Execute();
if (mDict1 != null) mDict1.TryGetValue("testRFC3", out ent);
ent.Execute();
}
[RFC]
public void testRFC()
{
Console.WriteLine("call RFC");
}
[RFC]
public void testRFC2()
{
Console.WriteLine("hello world");
}
[RFC]
public void testRFC3(string hello)
{
Console.WriteLine("say " + hello);
}
[System.NonSerialized]
static Dictionary<System.Type, System.Collections.Generic.List<CachedMethodInfo>> mMethodCache =
new Dictionary<System.Type, System.Collections.Generic.List<CachedMethodInfo>>();
// Cached RFC functions
[System.NonSerialized] static Dictionary<int, CachedFunc> mDict0 = new Dictionary<int, CachedFunc>();
[System.NonSerialized] static Dictionary<string, CachedFunc> mDict1 = new Dictionary<string, CachedFunc>();
/// <summary>
/// 反射獲取MainClass的RFC, 保存到列表中
/// </summary>
private static void BuildMethodList(MainClass mb)
{
var type = mb.GetType();
System.Collections.Generic.List<CachedMethodInfo> ret;
if (!mMethodCache.TryGetValue(type, out ret))
{
ret = new System.Collections.Generic.List<CachedMethodInfo>();
var cache = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
for (int b = 0, bmax = cache.Length; b < bmax; ++b)
{
var ent = cache[b];
if (!ent.IsDefined(typeof(RFC), true)) continue;
var ci = new CachedMethodInfo();
ci.name = ent.Name;
ci.rfc = (RFC)ent.GetCustomAttributes(typeof(RFC), true)[0];
ci.cf = new CachedFunc();
ci.cf.mi = ent;
ret.Add(ci);
}
mMethodCache.Add(type, ret);
}
for (int b = 0, bmax = ret.Count; b < bmax; ++b)
{
var ci = ret[b];
var ent = new CachedFunc();
ent.obj = mb;
ent.mi = ci.cf.mi;
if (ci.rfc.id > 0)
{
if (ci.rfc.id < 256) mDict0[ci.rfc.id] = ent;
else Console.WriteLine("RFC IDs need to be between 1 and 255 (1 byte). If you need more, just don't specify an ID and use the function's name instead.");
mDict1[ci.name] = ent;
}
else if (ci.rfc.property != null)
{
//mDict1[ci.name + "/" + ci.rfc.GetUniqueID(mb)] = ent;
}
else mDict1[ci.name] = ent;
}
}
}
public struct CachedMethodInfo
{
public string name;
public CachedFunc cf;
public RFC rfc;
}
/// <summary>
/// Remote Function Call attribute. Used to identify functions that are supposed to be executed remotely.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class RFC : Attribute
{
/// <summary>
/// Optional RFC ID, which should be in 1-254 range (inclusive). For example: [RFC(123)]. This is useful for frequent packets,
/// as using tno.Send(123, ...) requires less bytes than tno.Send("RFC_name", ...) -- however in vast majority of the cases,
/// it's not advisable to use IDs as it makes debugging more difficult just to save a few bytes per packet.
/// </summary>
public int id = 0;
/// <summary>
/// Name of the optional property that will be used to uniquely identify this RFC in addition to its name. This can be useful if you have
/// multiple RFCs with an identical name underneath the same TNObject. For example, in Project 5: Sightseer, a vehicle contains multiple
/// attachment points, with each attachment point having a "set installed item" RFC. This is done by giving all attachment points a unique
/// identifier, ("uniqueID"), which is basically a public field set in inspector on the vehicle's prefab (but can also be a property).
///
/// RFCs then look like this:
/// [RFC("uniqueID")] void MyRFC (...);
///
/// The syntax to send an RFC to a specific uniquely-identified child is like this:
/// tno.Send("MyRFC/" + uniqueID, ...);
/// </summary>
public string property;
public RFC(string property = null)
{
this.property = property;
}
public RFC(int rid)
{
id = rid;
property = null;
}
}
/// <summary>
/// Functions gathered via reflection get cached along with their object references and expected parameter types.
/// </summary>
public class CachedFunc
{
public object obj = null;
public MethodInfo mi;
ParameterInfo[] mParams;
Type[] mTypes;
int mParamCount = 0;
bool mAutoCast = false;
public ParameterInfo[] parameters
{
get
{
if (mParams == null)
{
if (mi == null) return null;
mParams = mi.GetParameters();
mParamCount = parameters.Length;
}
return mParams;
}
}
/// <summary>
/// Execute this function with the specified number of parameters.
/// </summary>
public object Execute(params object[] pars)
{
if (mi == null) return null;
var parameters = this.parameters;
if (pars == null && mParamCount != 0) pars = new object[parameters.Length];
if (mParamCount == 1 && parameters[0].ParameterType == typeof(object[])) pars = new object[] { pars };
try
{
return mi.Invoke(obj, pars);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
}
}