前言:反射在C#中虽然不常用(如果不需要动态加载xx.dll),但是有时候却是设计某个程序或者完成某类功能比较好用的技术。比如:一个支持动态扩展的程序,这样就需要动态加载dll,动态创建加载dll的程序集,最终完成操作。

一、加载程序集

对于程序集的加载一般会使用两个方法来进行:

1.Assembly.Load(string assemblyName),AssemblyName为程序集的长格式名称。

      Assembly SampleAssembly = Assembly.Load("SampleAssembly, Version=1.0.2004.0, Culture=neutral, PublicKeyToken=8744b20f8da049e3");
foreach (Type oType in SampleAssembly.GetTypes()) {
Console.WriteLine(oType.Name);
}

2.Assembly.Load(AssemblyName assemblyName),assemblyName为完整描述程序集的唯一标识,通过Assembly.GetName()方法可得到程序集的唯一标识。

3.Assembly.LoadFile(string path),加载指定路径上的程序集文件的内容。

4.Assembly.LoadFrom(string assemblyFile),已知程序集的文件名或路径,加载程序集。

       Assembly SampleAssembly;
SampleAssembly = Assembly.LoadFile(@"C:\Users\Admin\Documents\visual studio 2013\Projects\ConsoleApplication3\Sample\bin\Debug\Sample.dll");
MethodInfo Method = SampleAssembly.GetTypes()[].GetMethod("DoWork");
ParameterInfo[] Params = Method.GetParameters();
foreach (ParameterInfo Param in Params)
{
Console.WriteLine("Param=" + Param.Name.ToString());
Console.WriteLine(" Type=" + Param.ParameterType.ToString());
Console.WriteLine(" Position=" + Param.Position.ToString());
Console.WriteLine(" Optional=" + Param.IsOptional.ToString());
}

LoadFile和LoadFrom方法在大部分情况下是一样的结果,但是还是有区别的,如下:

、Assembly.LoadFile只载入相应的dll文件,比如Assembly.LoadFile("a.dll"),则载入a.dll,假如a.dll中引用了b.dll的话,b.dll并不会被载入。
Assembly.LoadFrom则不一样,它会载入dll文件及其引用的其他dll,比如上面的例子,b.dll也会被载入。
、用Assembly.LoadFrom载入一个Assembly时,会先检查前面是否已经载入过相同名字的Assembly,比如a.dll有两个版本(版本1在目录1下,版本2放在目录2下),程序一开始时载入了版本1,当使用Assembly.LoadFrom("2\\a.dll")载入版本2时,不能载入,而是返回版本1。Assembly.LoadFile的话则不会做这样的检查。

除了上述使用外,还可以从一个url中加载一个dll文件:

SampleAssembly = Assembly.LoadFrom("http://http://www.cnblogs.com/ListenFly/Sample.dll");

虽然此地址不存在,但是此方式是可行的。

5.仅仅加载程序集

如果构建的工具只是通过反射来分析程序元数据,并希望确保程序集中的任何代码都不会执行,那么加载程序集的最佳方式就是使用Assembly的ReflectionOnlyLoadFrom方法或者使用ReflectionOnlyLoad。

ReflectionOnlyLoadFrom方法将加载由路径指定的文件;和Load不同的是,ReflectionOnlyLoad方法不会应用版本控制策略,所以你指定的是哪个版本,获得的就是哪个版本。

用ReflectionOnlyLoadFrom或ReflectionOnlyLoad方法加载程序集时,CLR禁止程序集中的任何代码执行;试图执行由这两个方法加载的程序集中的代码,会导致CLR抛出异常。

6.将dll和exe打包在一起,并加载

有时候我们只会发布一个exe,甚至说不用安装直接运行就可以的软件,这时候就可以将要引用的dll添加到项目中,并且设置文件的属性(Properties)中的Build Action(生成操作)设置为嵌入资源(Embed Resource)。在运行时,CLR会找不到依赖的DLL程序集。为了解决这个问题,当应用程序初始化时,向AppDomain的ResolveAssembly事件登记一个回调方法,如下:

      AppDomain.CurrentDomain.AssemblyResolve +=(sender, e) =>
{
string resourceName = "AssemblyLoadingAndReliection." +
new AssemblyName(e.Name) + ".dll";
using (var stream=Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
Byte[] assemblyData=new Byte[stream.Length];
stream.Read(assemblyData, , assemblyData.Length);
return Assembly.Load(assemblyData);
}
};

现在,一个线程首次调用一个方法时,如果发现该方法引用了依赖DLL文件中的一个类型,就会引发一个AssemblyResolve事件,而上述回调代码会找到所需的嵌入DLL资源,并调用Assembly的Load方法的一个重载版本(传递一个Byte[]实参),从而加载所需的资源。

二、关于反射的性能

虽然反射相当的强大,允许在运行时发现并使用编译时还不了解的类型及其成员。但是有两个缺点:

1.反射会造成编译时无法保证类型安全性,由于反射要严重依赖字符串,所以会丧失编译时的类型安全性。

2.反射速度慢。使用反射时,类型及其成员的名称在编译时未知;要用字符串名称标识每个类型及其成员,以便在运行时发现他们。也就是说,需要扫描程序集的元数据,并且要不断地执行字符串。

基于上述所有原因,最好避免利用发射来访问字段或者调用方法/属性。如果要写一个应用程序来动态发现和构造类型实例,应采取以下两种技术之一。

1.让类型从一个编译时已知的基类型派生。在运行时,构造派生类型的一个实例,将对它的引用放到基类型的一个变量中,再调用基类型定义的虚方法。

2.让类型实现一个编译时已知的接口。在运行时,构造派生类型的一个实例,将对它的引用放到接口的一个变量中,再调用接口定义的方法。

方法1可以用来控制类的版本,因为随时都能向基类添加一个成员,派生类则直接继承;方法2则是从功能上选择一个比较好的对象来操作。

三、反射程序集中的类型

1.发现程序集中的类型

反射经常用于判断一个程序集中定义了哪些类型。最常用的方法是Assembly的GetExportedTypes。

        string assemblyName = @"System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
Assembly assembly = Assembly.Load(assemblyName);
foreach (var t in assembly.GetExportedTypes())
{
Console.WriteLine(t.FullName);
}

2.GetType方法

Object.GetType方法返回当前类型的RuntimeType对象的一个引用。此方法可以用来判断当前类型是否是某种类型:

if(o.GetType() == typeof(MyType))

除了上述方式  is 关键字也可以用来判断对象是否是某种类型。

3.构造类型的实例

  • System.Activator的CreateInstance方法,Activator提供了CreateInstance静态方法,此方法可以传递一个Type对象,也可以传递标识了想要创建的类型的一个string。
    string的重载,第一个参数为程序集名称,第二个参数为类型名称(完全限定名,即namespace+typeName)。

       ObjectHandle handle = Activator.CreateInstance("PersonInfo", "Person");
    Person p = (Person) handle.Unwrap();

    type参数重载,调用指定类型的默认构造函数。

    objct o= Activator.CreateInstance(typeof(object));

    上述方法返回的不是新对象的引用,而是一个System.Runtime.Remoting.ObjectHanlde对象(派生自System.MarshalByRefObject)。ObjectHandle类型允许将一个AppDomain中创建的对象传至其他AppDomain,期间不强迫对象具体化。所以要具体化对象时,请调用ObjectHandle的Unwrap方法。

  • System.Activator的CreateInstanceFrom方法,Activator提供了一组静态的CreateInstanceFrom方法。这些方法与CreateInstance行为相似,只是必须通过字符串来指定类型及其程序集。同样要使用ObjectHandle的Unwrap方法进行具体化对象。
  • System.AppDomain的方法 AppDomain提供了4个用于构造类型实例的实例方法:CreateInstance、CreateInstanceAndUnwrap、CreateInstanceFrom以及CreateInstanceFromAndUnwrap。这些方法的行为和Activator的行为类似,只是它们都是实例方法,允许指定在哪个AppDomain中构造对象。另外,带Unwrap后缀的方法还能简化操作。
  • System.Type的InvokeMember实例方法,可以使用一个Type对象引用来调用InvokeMember方法。System.Reflection.ConstructorInfo的Invoke实例方法,使用一个Type对象引用,可以绑定到一个特定的构造器,并获取对构造器的ConstructorInfo对象的一个引用。然后可以利用对这个ConstructorInfo对象的引用调用它的Invoke方法。

构造泛型:

       Type openType = typeof(Dictionary<,>);
Type closedType = openType.MakeGenericType(typeof(string), typeof(int));
object o = Activator.CreateInstance(closedType);
Console.WriteLine(o.GetType());

4.设计一个支持加载项的应用程序

一个类库Host,包含自己的接口

namespace Host
{
public interface IMyInstance
{
string DoWork(int parameter);
}
}

实现接口的两个类(在类库Sample中定义):

public class MyClass1 : IMyInstance
{
public string DoWork(int parameter)
{
return "Class1:" + parameter.ToString();
}
}
 public class MyClass2:IMyInstance
{
public string DoWork(int parameter)
{
return "Class2:" + parameter.ToString();
}
}

加载程序所引用的dll文件,并加载所有dll的程序集,最终找到实现自IMyInstance接口的类:

  static void Main(string[] args)
{
//获取当前运行程序的路径
var hostDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
//加载路径中的所有dll
string[] hostAssemblies = Directory.GetFiles(hostDir, "*.dll");
List<Type> hostTypes = new List<Type>(); foreach (var file in hostAssemblies)
{
//将dll文件加载到程序集中
Assembly hostAssembly = Assembly.LoadFrom(file);
foreach (var type in hostAssembly.GetExportedTypes())
{
//确定type为类并且继承自(实现)IMyInstance
if (type.IsClass && typeof(IMyInstance).IsAssignableFrom(type))
hostTypes.Add(type);
}
} foreach (var type in hostTypes)
{
IMyInstance instance = (IMyInstance)Activator.CreateInstance(type);
Console.WriteLine(instance.DoWork());
} Console.Read();
}

当前假设,Main函数所在的程序,添加了Host和Sample类库,所以在Debug中有两个dll文件。

四、使用反射发现类型的成员

1.发现类型成员

字段、构造器、方法、属性、事件和嵌套类型都可以被定义为一个类型的成员。FCL包含一个名为System.Reflection.MemberInfo类型。此类型为抽象类,封装一组所有类型成员的通用属性。从MemberInfo派生的是一组类,每个类都封装了与一个特定类型成员相关的更多属性。

  static void Main(string[] args)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
WritLine(, "Assembly:{0}", assembly);
foreach (var type in assembly.GetExportedTypes())
{
BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.NonPublic |
BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
foreach (var memberInfo in type.GetMembers(flags))
{
var typeName = string.Empty;
if (memberInfo is Type) typeName = "Nested Type";
else if (memberInfo is FieldInfo) typeName = "FieldInfo";
else if (memberInfo is MethodInfo) typeName = "MethodInfo";
else if (memberInfo is ConstructorInfo) typeName = "ConstructorInfo";
else if (memberInfo is PropertyInfo) typeName = "PropertyInfo";
else if (memberInfo is EventInfo) typeName = "EventInfo";
WritLine(, "{0}: {1}", typeName, memberInfo);
}
}
}
Console.Read();
}
static void WritLine(int indent, string format, params object[] args)
{
Console.WriteLine(new string(' ', * indent) + format, args);
}

上述为加载当前AppDomain中的所有程序集、以及其类型和类型的成员,并输出。

2.BindingFlags筛选返回的成员种类

可以使用Type的GetMembers、GetNextedTypes、GetFields、GetConstructors、GetMethods、GetProperties、GetEvents方法查询一个类型的成员。使用上述方法时,可以传递BindingFlags枚举类型的一个实例。这个类型标识了一组通过逻辑OR运算合并到一起的位标识(通过在枚举添加  [Flags]实现),默认设置是BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static(如果指定了Public或者NonPublic,那么必须同时指定Instance | Static,否则将不返回成员)。

3.发现类型的接口

通过Type类型的FindInterfaces、GetInterface或者GetInterfaces方法。所有这些方法都返回代表接口的Type对象。为了获得一个特定接口的MethodInfo对象,可以调用Type的GetInterfaceMap实例方法(传递接口类型作为参数)。该方法返回System.Reflection.InterfaceMapping的一个实例。

InterfaceMapping类型的公共字段:

TargetType Type类型 用于调用GetInterfaceMapping的类型

InterfaceType Type类型 传给GetInterfaceMapping的接口类型

InterfaceMethods MethodInfo[]类型 一个数组,每个元素表示接口中的一个方法的信息

TargetMethods    MethodInfo[]类型 一个数组,每个元素表示由当前类型实现自接口的一个方法

InterfaceMethods和TargetMethods数组是相互对应的。也就是说InterfaceMethods[0]表示的是接口的MethodInfo,那么TargetMethods[0]表示的是类型定义的方法(实现自接口)。

下面一个发现类型实现的接口的例子:

   interface IBookRetailer:IDisposable
{
void Purchase();
void ApplyDiscount();
}
    interface IMusicRetailer
{
void Pruchase();
}
 public class MyRetailer:IBookRetailer,IMusicRetailer
{
/// <summary>
/// MyRetailer类自己的Purchase方法
/// </summary>
public void Purchase()
{
} /// <summary>
/// IMusicRetailer的Pruchase方法
/// </summary>
void IMusicRetailer.Pruchase()
{
} /// <summary>
/// IBookRetailer的Purchase方法
/// </summary>
void IBookRetailer.Purchase()
{
} public void ApplyDiscount()
{
} public void Dispose()
{
}
}

上述代码定义了两个接口一个类,然后让这个类实现两个接口,这点都没有任何特殊。特殊就在于,两个接口拥有同样的一个方法就是Pruchase,同时类本身也有一个这样名称的方法,防止出现方法的冲突,这里使用了显示实现接口,即方法的前缀为具体的接口,这样虽然在类中有3个Pruchase方法,但是由于是指定了其所属接口,所以是没有问题的。

 static void Main(string[] args)
{
Type type = typeof(MyRetailer);
Type[] interfaces = type.FindInterfaces(TypeFilter, typeof(Program).Assembly);
Console.WriteLine("MyRetailer implements the following " +
" instances (defined in this assemly) :");
foreach (var interfaceType in interfaces)
{
Console.WriteLine("\nInterface:" + interfaceType);
//获取映射接口方法的类型的方法
InterfaceMapping mapping = type.GetInterfaceMap(interfaceType); for (int i = ; i < mapping.InterfaceMethods.Length; i++)
{
Console.WriteLine(" {0} is Implemented by {1}", mapping.InterfaceMethods[i], mapping.TargetMethods[i]);
}
}
Console.Read();
} /// <summary>
/// 如果类型匹配筛选器条件,就返回true
/// </summary>
/// <param name="t"></param>
/// <param name="filterCriteria"></param>
/// <returns></returns>
private static bool TypeFilter(Type t, object filterCriteria)
{
//如果接口和filterCriteria标识的程序集中定义的,就返回true
return t.Assembly == filterCriteria;
}

上述代码用于查找MyRetailer的所有接口,FindInterfaces有两个参数,第一个为返回值为bool类型的过滤方法,第二个参数为方法的参数。我们的TypeFilter即为第一个方法,有两个参数,一个为Type(自动判断,根据当前调用此方法的类型的接口类型),第二个就是参数了。在方法中判断,只有接口的程序集和指定的程序集一直才认为是true。
通过FindInterfaces得到Type数组,然后使用Type.GetInterfaceMap得到接口和类型的映射,最后根据InterfaceMethods和TargetMethods输出实现的信息。

P.S. 是不是发现输出的信息中并没有IDisposable接口的信息,那是因为在TypeFilter方法中我们过滤了和指定的程序集不一致的接口,而IDisposable当然不和Exe在同一个程序集,所以就没有查找到。

4.调用类型的成员

通过反射得到类型的成员是没有太多意义的,我们更多地是去操作成员。比如,可以调用FieldInfo进行设置或获取字段的值;调用ConstructorInfo,访问构造函数,并获得实例;也可以调用一个MethodInfo,进行执行方法,如果有返回值则可以得到;PropertyInfo可以调用属性的get和se方法;EventInfo可以添加或者删除一个事件。

上述的所有操作,我们都可以使用Type.InvokeMember方法进行实现:

  public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, CultureInfo culture);

name:成员名称;invokeAttr:如何查找成员;binder:如何匹配成员和实参;target:要调用其成员的对象;args:要传给方法的实参;culture:某些绑定器使用的语言文化。

binder表示执行InvokeMember方法时选择成员时候使用的规则,从候选者列表中选择一个成员,并执行实参类型到形参类型的类型转换,如果传递null则默认使用DefauleBinder,当然我们可以自己定义新的Binder(详见MSDNBinder类介绍)。

下表列出了默认联编程序支持的转换。

 

源类型

目标类型

任何类型

它的基类型。

任何类型

它实现的接口。

Char

Unt16、UInt32、Int32、UInt64、Int64、Single、Double

Byte

Char、Unt16、Int16、UInt32、Int32、UInt64、Int64、Single、Double

SByte

Int16、Int32、Int64、Single、Double

UInt16

UInt32、Int32、UInt64、Int64、Single、Double

Int16

Int32、Int64、Single、Double

UInt32

UInt64、Int64、Single、Double

Int32

Int64、Single、Double

UInt64

Single、Double

Int64

Single、Double

Single

Double

5.一次绑定,多次调用

使用Type的InvokeMember方法可以访问一个类型的所有成员。但是,应该注意,每次调用InvokeMember方法时,它都必须绑定到一个特定的成员,然后才能调用它。如果每次调用一个成员都进行这个操作,那么性能肯定是会受到影响。所以,如果打算频繁访问一个成员,最好是一次绑定,多次调用。

绑定成员后,如果调用成员:

FieldInfo  调用GetValue获取字段的值;调用SetValue设置字段的值。

ConstructorInfo 调用Invoke构造类型的一个实例,并调用构造函数。

MethodInfo 调用Invoke调用类型的一个方法。

PropertyInfo 调用GetValue调用属性的get访问器方法;调用SetValue调用属性的set构造器方法。

EventInfo 调用AddEventHandler调用事件的add访问器方法;调用RemoveEventHanlder调用事件的remove访问器方法。

此外,EventInfo还提供了GetAddMethod和GetRemoveMethod方法,都返回一个MethodInfo,这个MethodInfo对应事件添加或删除委托的方法。要添加或删除一个委托,可调用这些MethodInfo对象,也可调用EventInfo类型提供的AddEventhandler和RemoveEventHanlder方法。

下面例子演示了使用反射来范文类型成员的各种方式:

SomeType类:

  class SomeType
{
private int m_someField;
public SomeType(ref int x) { x *= ; }
public override string ToString()
{
return m_someField.ToString();
} public int SomeProp
{
get { return m_someField; }
set
{
if (value < )
throw new ArgumentOutOfRangeException("值不在范围之内");
m_someField = value;
}
} public event EventHandler SomeEvent;
private void NoCompilerWarnings()
{
SomeEvent.ToString();
}
}

此类有多个成员,一个私有变量,一个公共属性,一个公共构造器(以引用方式传递的int类型参数),一个公用方法,以及一个公共事件。

一共使用四种方式来访问SomeType的成员:

  • UseInvokeMemberToBindAndInvokeTheMember方法演示了利用Type的InvokeMember来绑定并调用一个成员。
  • BindToMemberThenInvokeTheMember方法演示了如何绑定到一个成员,并在以后调用它。如果打算在不同对象上多次调用同一个成员,那么这个方法可以提高性能。
  • BindToMemberCreateDelegateToMemberThenInvokeTheMember方法演示了如何绑定到一个对象或成员,然后创建一个委托来引用该对象或成员。通过委托来调用的速度非常快。如果想在相同的对象上多次调用相同的成员,那么此技术比上一个技术速度还要快。
  • UseDynamicToBindAndInvokeTheMember方法演示了如何使用C#的dynamic(.Net 4.0新特性)基元类型来简化访问成员时的语法。另外,如果打算在相同类型的不同对象上调用相同的成员,此技术性能还不错,因为针对每个类型,绑定都只会发生一次,而且可以缓存起来,以后多次调用时速度会很快。还可以使用此技术调用不用类型的对象的成员。
    private static void UseInvokeMemberToBindAndInvokeTheMember(Type t)
{
Console.WriteLine("UseInvokeMemberToBindAndInvokeTheMember"); //构造一个Type的实例
object[] args = new object[] { };
Console.WriteLine("x before constructor called:" + args[]);
object obj = t.InvokeMember(null, flags | BindingFlags.CreateInstance, null, null, args);
Console.WriteLine("Type: " + obj.GetType().ToString());
Console.WriteLine("x after constructor returns:" + args[]); //读写一个字段
t.InvokeMember("m_someField", flags | BindingFlags.SetField, null, obj, new object[] { });
int value = Convert.ToInt16(t.InvokeMember("m_someField", flags | BindingFlags.GetField, null, obj, null));
Console.WriteLine("someField:" + value); //调用一个方法
string str = (string)t.InvokeMember("ToString", flags | BindingFlags.InvokeMethod, null, obj, null);
Console.WriteLine("ToString:" + str); //读写一个属性
try
{
t.InvokeMember("SomeProp", flags | BindingFlags.SetProperty, null, obj, new object[] { });
}
catch (TargetInvocationException)
{
Console.WriteLine("Set Property Catch!");
} t.InvokeMember("SomeProp", flags | BindingFlags.SetProperty, null, obj, new object[] { });
value = Convert.ToInt16(t.InvokeMember("SomeProp", flags | BindingFlags.GetProperty, null, obj, null));
Console.WriteLine("SomeProp:" + value); //调用事件add/remove方法,为事件添加和删除一个委托
EventHandler handler = new EventHandler(EventCallback);
t.InvokeMember("add_SomeEvent", flags | BindingFlags.InvokeMethod, null, obj, new object[] { handler });
t.InvokeMember("remove_SomeEvent", flags | BindingFlags.InvokeMethod, null, obj, new object[] { handler });
} private static void BindToMemberThenInvokeTheMember(Type t)
{
//构造一个实例,之所以GetConstructor的参数为MakeByRefType,那是因为SomeType
//的构造函数的参数为ref引用传递
ConstructorInfo constructorInfo = t.GetConstructor(new Type[] { typeof(Int32).MakeByRefType() });
object[] args = new object[] { };
Console.WriteLine("x before constructor called: " + args[]);
object obj = constructorInfo.Invoke(args);
Console.WriteLine("Type:" + obj.GetType().ToString());
Console.WriteLine("x after constructor returns:" + args[]); //读写一个字段
FieldInfo fieldInfo = obj.GetType().GetField("m_someField",flags);
fieldInfo.SetValue(obj, );
Console.WriteLine("someField:" + fieldInfo.GetValue(obj)); //调用一个方法
MethodInfo methodInfo = obj.GetType().GetMethod("ToString", flags);
string str = (string)methodInfo.Invoke(obj, null);
Console.WriteLine("ToString:" + str); //读写一个属性
PropertyInfo propertyInfo = obj.GetType().GetProperty("SomeProp", typeof(Int32));
try
{
propertyInfo.SetValue(obj, , null);
}
catch (Exception)
{
Console.WriteLine("Property set catch!");
} propertyInfo.SetValue(obj, , null);
Console.WriteLine("SomeProp:" + propertyInfo.GetValue(obj, null)); //为事件添加和删除一个委托
EventInfo eventInfo = obj.GetType().GetEvent("SomeEvent", flags);
EventHandler handler = new EventHandler(EventCallback);
eventInfo.AddEventHandler(obj, handler);
eventInfo.RemoveEventHandler(obj, handler); } private static void BindToMemberCreateDelegateToMemberThenInvokeTheMember(Type t)
{
Console.WriteLine("BindToMemberCreateDelegateToMemberThenInvokeTheMember"); //构造一个实例(不能创建对构造函数的委托)
object[] args = new object[] { };
Console.WriteLine("x before constructor called:" + args[]);
object obj = Activator.CreateInstance(t, args);
Console.WriteLine("Type:" + obj.GetType().ToString());
Console.WriteLine("x after constructor returns:" + args[]); //调用一个方法
MethodInfo methodInfo = obj.GetType().GetMethod("ToString", flags);
var toString = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>), obj, methodInfo);
string str = toString();
Console.WriteLine("ToString:" + str); //读写一个属性
PropertyInfo propertyInfo = obj.GetType().GetProperty("SomeProp", typeof(Int32));
var setSomeProp = (Action<Int32>)Delegate.CreateDelegate(typeof(Action<Int32>), obj, propertyInfo.GetSetMethod());
try
{
setSomeProp();
}
catch (Exception)
{
Console.WriteLine("Property set catch!");
}
setSomeProp();
var getSomeProp = (Func<Int32>)Delegate.CreateDelegate(typeof(Func<Int32>), obj, propertyInfo.GetGetMethod());
Console.WriteLine("SomeProp:" + getSomeProp()); //从事件中添加和删除一个委托
EventInfo eventInfo = obj.GetType().GetEvent("SomeEvent", flags);
var addSomeEvent = (Action<EventHandler>)Delegate.CreateDelegate(typeof(Action<EventHandler>), obj, eventInfo.GetAddMethod());
addSomeEvent(EventCallback);
var removeSomeEvent = (Action<EventHandler>)Delegate.CreateDelegate(typeof(Action<EventHandler>), obj, eventInfo.GetRemoveMethod());
removeSomeEvent(EventCallback);
} private static void UseDynamicToBindAndInvokeTheMember(Type t)
{
Console.WriteLine("UseDynamicToBindAndInvokeTheMember");
//构造一个实例(不能创建对构造器的委托)
object[] args = new object[] { };
Console.WriteLine("x before constructor called:" + args[]);
dynamic obj = Activator.CreateInstance(t,args);
Console.WriteLine("Type:" + obj.GetType().ToString());
Console.WriteLine("x after constructor returns:" + args[]); //读写一个字段
try
{
obj.m_someField = ;
int value = Convert.ToInt16(obj.m_someField);
Console.WriteLine("someField:" + value);
}
catch (Exception ex)
{
//字段是私有的,so会出错的
Console.WriteLine("Failed to access field:" + ex.Message);
} //调用一个方法
string str = obj.ToString();
Console.WriteLine("ToString:" + str); //读写一个属性
try
{
obj.SomeProp =;
}
catch (Exception)
{ Console.WriteLine("Property set catch!");
}
obj.SomeProp = ;
int v = Convert.ToInt16(obj.SomeProp);
Console.WriteLine("SomeProp:", v); //添加删除事件
obj.SomeEvent += new EventHandler(EventCallback);
obj.SomeEvent -= new EventHandler(EventCallback);
} /// <summary>
/// 事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void EventCallback(object sender, EventArgs e)
{ }

好了,反射知识暂告一段落,希望多提意见和建议。

C#之你懂得的反射的更多相关文章

  1. Java程序员都要懂得知识点:反射

    摘要:Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语 ...

  2. C#中通过反射方法获取控件类型和名称

    这个方法是简单的也是神奇的. 有木有想过,将自己项目中的所有类型,包括自定义类型的命名空间和名称全部获取出来? 有木有想过,有一种简便的方法可以自动化管理项目中的控件和窗体? 有木有想过... 首先, ...

  3. Java基础--反射机制的知识点梳理

    什么是反射? 正常编译执行java文件时,会生成一个.class文件,反射就是一个反编译的过程,它可以通过.class文件得到一个java对象.一个类会有很多组成部分,比如成员变量,成员方法,构造方法 ...

  4. .Net 中的反射(序章) - Part.1

    引言 反射是.Net提供给我们的一件强力武器,尽管大多数情况下我们不常用到反射,尽管我们可能也不需要精通它,但对反射的使用作以初步了解在日后的开发中或许会有所帮助. 反射是一个庞大的话题,牵扯到的知识 ...

  5. Java 类反射机制分析

    Java 类反射机制分析 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某 ...

  6. python 反射调用

    因为目前在写一个python的项目,用到了Python的反射机制,所以做一下笔记,把写项目过程中的感悟记下来. 先简单介绍下Demo用到的函数: sys.path 是python的模块的路径集,是一个 ...

  7. Unity shader(CG) 写一个 散色、折射、反射、菲涅尔、gamma、简单后期屏幕特效

    http://www.lai18.com/content/506918.html 1.自生要求是很重要的,当然不是什么强迫工作之类的,而是自己有限的能力上不断的扩展兴趣上的内容. 2.用生活的眼光去发 ...

  8. 浅说Java中的反射机制(二)

    写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编 ...

  9. Android反射机制实现与原理

    本文介绍Android反射机制实现与原理,在介绍之前,要和Java进行比较,所以先看下Java中的反射相关知识: 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或 ...

随机推荐

  1. Oracle EBS中查询Profile的各种SQL【转载】

    1.List E-Business Suite Profile Option Values For All Levels SELECT p.profile_option_name SHORT_NAME ...

  2. 第九章 CSS-DOM

    另一个网友整理了很多书中的代码:http://www.cnblogs.com/jingangel/archive/2013/01/03/2843505.html 1. 三位一体的网页 浏览器看到的网页 ...

  3. Jquery结合Ztree生成树

    尊重作者,附原文链接:http://my.oschina.net/u/2472104/blog/529055 Ztree的api http://www.ztree.me/v3/api.php Ztre ...

  4. 重新认识Box Model、IFC、BFC和Collapsing margins

    尊重原创,转载自: http://www.cnblogs.com/fsjohnhuang/p/5259121.html 肥子John^_^ 前言   盒子模型作为CSS基础中的基础,曾一度以为掌握了I ...

  5. Extjs中TextField中显示图标

    现看实现效果:   实现代码: { width:500, height : 25, fieldLabel : '过滤条件', itemId : 'queryText', labelAlign : 'r ...

  6. 【转载】oracle 分区表详解

    一.分区表的概述:     Oracle的表分区功能通过改善可管理性.性能和可用性,从而为各式应用程序带来了极大的好处.通常,分区可以使某些查询以及维护操作的性能大大提高.此外,分区还可以极大简化常见 ...

  7. shell编程之环境变量

    在shell编程里我们首先接触到的是环境变量,常用命令说明 1. 使用echo命令查看单个环境变量.例如: echo $PATH 2. 使用env查看所有环境变量.例如: env 3. 使用set查看 ...

  8. 【BZOJ】【1874】取石子游戏

    SG函数 嗯博弈论入门题,关于SG函数这个东西可以去看VFK神犇的博客,讲的非常清楚Orz. 传送门:vfleaking.blog.163.com/blog/static/17480763420123 ...

  9. apple 官方文档 Push Notification Programming

    iOS Developer LibraryDeveloper Search Local and Push Notification Programming Guide PDF Table of Con ...

  10. Kali Linux 命令集

    系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...