反射
定义
- 类型 Type:描述程序集,类型等的元数据信息
- 反射 Reflect:
一个运行时获取元数据信息的过程,得到一个给定程序集所包含的所有类型的列表,包括给定类型的方法,字段,属性和事件,接口,方法的参数和其他相关细节(基类,命名空间,清单数据)
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");
查看元数据
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>
/// 反射方法 参数 和 返回值
/// </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 = "( ";
//得到参数
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 动态加载程序集,反射获取程序集的元数据
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)
定义
创建一个给定类型的实例,并在运行时调用其方法,而不需要在编译期知道它存在的一种技术
方法
System.Activator.CreateInstance() 创建运行时实例
MethodInfo.Invoke() 执行实例的方法
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);
//调用无参数函数
MethodInfo hello = myClass.GetMethod("hello");
Console.WriteLine("call {0} ", hello.Invoke(obj, null));
//调用包含参数的函数
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:限定应用范围; 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 反射和特性的应用
- [RFC] 定义远程函数调用特性
- CachedFunc 封装MethodInfo.Invoke函数执行方法
- BuildMethodList 发射获取RFC 函数并保存到本地列表中
- 从本地列表中查询RFC函数执行
/// <summary>
/// TNet 反射 特性应用
/// 用RFC 特性
/// 利用反射机制 保存具有RFC的obj 和 函数
/// 然后网络层收到消息后执行函数
/// </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;
}
}
}
}
网友评论