反射手册笔记 3.使用对象

本章是上一章的继续,再获取到对象类型后,接下来做的事情。

第一部分 动态调用成员——调用方法,检索或更改属性,以及字段

    方法1:利用Info类调用类成员
       1.用MethodInfo类调用方法:
          object[] Invoke(object obj, Object[] parameters)
          其中,第1个参数obj,是对象的实例(静态方法相应参数为null);第2个参数parameters是要传递到方法中的参数数组;返回值为object类型,因此,要对结果进行强制类型转换。示例如下:
     public   class  MathClass
    {
        
public   int  Multiply( int  x,  int  y)
        {
            
return  x  *  y;
        }

        [STAThread]
        
static   void  Main()
        {
            MathClass objMath 
=   new  MathClass();
            
object [] paramArray  =  {  4 5  };

            MethodInfo method 
=  objMath.GetType().GetMethod( " Multiply " );
            
            
int  result  =  ( int )method.Invoke(objMath, paramArray);
        }
    }
其中, objMath.GetType() .GetMethod( " Multiply " )方法,默认搜索所有共有成员,可以使用其重载方法:
objMath.GetType() .GetMethod( " Multiply " , BindingFlag.NonPublic),从而获取非公有成员。

       2.用PropertyInfo类调用属性:
            GetValue(Object obj, object[] index)
            SetValue(Object obj, Object NewValue, object[] index)

            其中,index为索引器(静态设为null),obj为对象的实例(静态设为null),返回值同样为object类型。示例如下:
     public   class  Human
    {
        
private   string  strName;

        
private   string  Name
        {
            
get  {  return  strName; }
            
set  { strName  =  value; }
        }

        [STAThread]
        
static   void  Main()
        {
            Human newHuman 
=   new  Human();

            
// 得到私有属性Name类型对象
            PropertyInfo prop  =  newHuman.GetType().GetProperty( " Name " , BindingFlags.Instance  |  BindingFlags.NonPublic);

            
// 设置私有属性Name
             string  param  =   " Jax.Bao " ;
            prop.SetValue(newHuman, param, 
null );

            
// 获取私有属性Name
            Console.WriteLine(prop.GetValue(newHuman,  null ).ToString());
        }
    }

       3.用FieldInfo类调用字段:
            GetValue(Object obj)
            SetValue(Object obj, Object value)
           
FieldInfo类使用同PropertyInfo,只是没有Index罢了。示例略。

    方法2:利用InvokeMember()方法调用类成员
       3个重载,最常用的如下:
        Object InvokeMember(
            
string  name,                     // 要调用的对象名:可以是属性名/方法名/字段名
            BindingFlags invokeAttr,         // 搜索成员的条件,以及如何处理第一个参数:
                                            //如BindingFlags.InvokeMethod表示第一个参数为方法名;
                                                                                            //BindingFlags.GetProperty或 SetProperty 表示第一个参数为属性名;
                                            //BindingFlags.SetField或
GetField 表示第一个参数为字段名;
            Binder binder,                   // 一般设为null,则会使用默认的DefaultBinder,对提供的参数进行类型转换,从原类型转为目标类型
            Object target,                   // 调用成员的类型的实例(静态成员为null)
            Object[] args                    // 对方法而言,是方法参数数组;对字段/属性而言,获取时是null,设置时是NewValue
        );
      
       1.调用方法:
         MethodInfo类的Invoke()方法,不能直接处理重载方法,即无法判断使用哪个重载方法,而InvokeMember则可以自动找到匹配的重载方法。示例如下:
            Type mathType  =   typeof (System.Math);
            
object [] paramArray  =  {  5 8  };

            
int  result  =  ( int )mathType.InvokeMember( " Max " ,
                            BindingFlags.Public 
|  BindingFlags.InvokeMethod  |  BindingFlags.Static,
                           
null null , paramArray);

        当然,如果使用MethodInfo类的Invoke()方法处理重载方法,要辅助以SelectMethod()先找到对应的重载方法,然后才能调用。

       2.操纵属性
          得到属性用BindingFlags.GetProperty,同时参数数组为null;设置属性用BindingFlags.SetProperty,示例如下:
            Type humanType  =   typeof (Human);
            Human newHuman 
=   new  Human();

            
// 设置私有属性Name
             object [] paramArray  =  {  " Jax.Bao "  };
            humanType.InvokeMember(
" Name " ,
                            BindingFlags.NonPublic 
|  BindingFlags.Instance  |  BindingFlags.SetProperty,  null , newHuman, paramArray);

            
// 得到私有属性Name类型对象
             string  result  =  humanType.InvokeMember( " get_Name " ,
                            BindingFlags.NonPublic 
|  BindingFlags.Instance  |  BindingFlags.InvokeMethod,  null , newHuman,  null );

          补注:
            在MSIL中属性其实就是方法,所以对属性的操纵也可以使用如下方式(以get为例):
            humanType.InvokeMember( " get_Name " ,
                    BindingFlags.NonPublic 
|  BindingFlags.Instance  |  BindingFlags.InvokeMethod,  null , newHuman,  null );
            同理,可以使用MethodInfo.InvokeMethod()改写为:
            Human objHuman  =   new  Human();
            Object[] paramArray 
=  {  " Jax.Bao "  };

            MethodInfo method 
=  objHuman.GetType().GetMethod( " get_Name " );

            
string  result  =  ( string )method.Invoke(objHuman, paramArray);

       3.操纵字段
         基本上同于"操纵属性",不再多言。

    两种方法的比较:
      
Type的InvokeMember()方法灵活且功能强大,应该优先使用;仅当需要处理元数据时,才使用info类。


第二部分 用反射模拟委托功能
    之所以要模拟,是因为委托时类型安全的(编译期)——区别于反射(运行期),所以小型程序要尽量使用委托。
    委托不区分静态方法/实例方法——区别于反射。

    模拟代码如下:
     public   class  Invoker
    {
        
private  Type myType;
        
private  Object myObject;
        
private  String myMethod;


        
public  Invoker(Type targetType, String targetMethod)
        {
            myType 
=  targetType;
            myMethod 
=  targetMethod;
        }

        
public  Invoker(Object targetObject, String targetMethod)
        {
            myObject 
=  targetObject;
            myType 
=  targetObject.GetType();
            myMethod 
=  targetMethod;
        }

        
public  Object Invoke(Object[] args)
        {
            
if  (myType  !=   null   &&  myMethod  !=   null )
            {
                BindingFlags myBindingFlags 
=  BindingFlags.InvokeMethod  |  BindingFlags.Public;

                
if  (myObject  !=   null )
                {
                    myBindingFlags 
=  myBindingFlags  |  BindingFlags.Static;
                }
                
else
                {
                    myBindingFlags 
=  myBindingFlags  |  BindingFlags.Instance;
                }

                
return  myType.InvokeMember(myMethod, myBindingFlags,  null , myObject, args);
            }
            
else
            {
                
throw   new  Exception( " Valid " );
            }
        }

    }

    
public   class  MyMath
    {
        
public   static   int  Pow( int  x,  int  y)
        { 
            
return  x  ^  y;
        }

        
public   int  Multiply( int  x,  int  y)
        {
            
return  x  *  y;
        }

        
static   void  Main()
        {
            
try
            {
                Object result;
                Object[] args 
=  {  2 3  };
                MyMath objMath 
=   new  MyMath();

                Invoker myDelegate 
=   new  Invoker(objMath,  " Multiply " );
                result 
=  myDelegate.Invoke(args);
                Console.WriteLine(
" The product of {0} and {1} is: {2} " , args[ 0 ], args[ 1 ], result);

                Invoker objDelegate 
=   new  Invoker( typeof (MyMath),  " Pow " );
                result 
=  objDelegate.Invoke(args);
                Console.WriteLine(
" The product of {0} and {1} is: {2} " , args[ 0 ], args[ 1 ], result);
            }
            
catch
            {
                
throw ;
            }
        }

    }

    在模拟中,委托可以使用委托链实现"反射"重载方法。



你可能感兴趣的:(反射)