• C#反射教程(5)


    在目录下新建一个程序文件,并命名为LateBinding.cs,编写代码如代码7.12所示。

    代码7.12 晚期绑定:LateBinding.cs

    +展开
    -C#
    using System;
    //导入相应的命名空间
    using System.Reflection;
    using System.IO;

    class LateBinding
    {
    static void Main(string[] args)
    {
    Console.Write("/n【1】请输入传递给OldClass类Method静态方法的参数:");
    string inputA = Console.ReadLine();
    Console.Write("/n【2】请输入传递给NewClass类Method方法的参数:");
    string inputB = Console.ReadLine();
    Console.Write("/n【3】请输入传递给MyClass类Method方法的参数:");
    string inputC = Console.ReadLine();
    Console.WriteLine("/n/t=======以下是不同程
    序集的不同方法调用结果=======/n");
    try
    {
    //将用户输入的3组值传递给以下3个方法
    LoadOldClass(inputA);
    LoadNewClass(inputB);
    LoadMyClass(inputC);
    }
    //捕获文件未找到异常
    catch (FileNotFoundException e)
    {
    //输出异常信息
    Console.WriteLine("异常信息:{0}", e.Message);
    }
    //捕获一般异常
    catch (Exception e)
    {
    //输出异常信息
    Console.WriteLine("异常信息:{0}", e.Message);
    }   
    }
    //定义LoadOldClass方法,接收一个string类型参数
    //该类用于加载OldClass程序集,并调用该程序集中OldClass类的Method静态方法
    static void LoadOldClass(string input)
    {
    //调用Assembly的Load方法,载入OldClass程序集
    Assembly am = Assembly.Load("OldClass");
    //获取OldClass类的Type对象
    Type OldTp = am.GetType("OldClass"falsefalse);
    //定义仅一个参数的数组OldList,子项初始化为input参数
    string[] OldList = new string[1] { input };
    //调用OldTp的InvokeMember方法,传递相应的参数
    //将方法返回结果转换为string类型并赋值给txt变量
    string txt = (string)OldTp.InvokeMember(
    "Method",
    BindingFlags.InvokeMethod,
    null,
    null,
    OldList
    );
    //输出txt变量
    Console.WriteLine(txt);
    }
    //定义LoadNewClass方法,接收一个string类型参数
    //该类用于加载NewClass程序集,并调用该程序集中NewClass类对象的Method实例方法
    static void LoadNewClass(string input)
    {
    //创建当前应用程序域的指定程序集中指定类型的实例
    object obj = AppDomain.CurrentDomain.
    CreateInstanceAndUnwrap("NewClass""NewClass");
    //调用obj的GetType方法,获取Type对象
    Type Newtp = obj.GetType();
    //定义仅一个参数的数组NewList,子项初始化为input参数
    string[] NewList = new string[1] { input };
    //调用Newtp的InvokeMember方法,传递相应的参数
    //将方法返回结果转换为string类型并赋值给txt变量
    string txt = (string)Newtp.InvokeMember(
    "Method",
    BindingFlags.InvokeMethod,
    null,
    obj,
    NewList
    );
    //输出txt变量
    Console.WriteLine(txt);
    }
    //定义LoadMyClass方法,接收一个string类型参数
    //该类用于加载MyClass程序集,并调用该程序集中MyClass类对象的Method实例方法
    static void LoadMyClass(string input)
    {
    //定义仅一个参数的数组MyList,子项初始化为input参数
    string[] MyList = new string[1] { input };
    //调用Assembly的Load方法,载入MyClass程序集
    Assembly MyAm = Assembly.Load("MyClass");
    //获取MyClass类的Type对象
    Type MyTp = MyAm.GetType("MyClass",false,false);
    //调用Activator类的CreateInstance方法
    //该方法可创建参数类型的实例
    object MyObj = Activator.CreateInstance(MyTp);
    //搜索MyTp的参数指定方法,并返回给mi变量
    MethodInfo mi = MyTp.GetMethod("Method");
    //根据参数调用mi的方法,并将返回结果赋值给txt变量
    string txt = (string)mi.Invoke(MyObj, MyList);
    //输出txt变量
    Console.WriteLine(txt);
    mi = MyTp.GetMethod("MethodTxt");
    //根据参数调用mi的方法,并将返回结果赋值给txt变量
    txt = (string)mi.Invoke(MyObj, null);
    //输出txt变量
    Console.WriteLine(txt);
    }
    }


      在命令行下将OldClass.cs、NewClass.cs和MyClass.cs编译为dll程序集,如图7.14所示。

    图7.14 编译外部程序集

      在命令行下编译LateBinding.cs,执行LateBinding程序,运行结果如图7.15所示。

    图7.15 晚期绑定
      本程序以多种情况展示了晚期绑定的编写方法,主程序的3大部分分别封装在LoadOldClass()、LoadNewClass()和 LoadMyClass()静态方法中。考虑到外部程序集有可能不存在,主程序编写了"文件未找到"的异常捕捉。LoadOldClass()和 LoadNewClass()方法的晚期绑定通过InvokeMember()方法调用对象的成员,而LoadMyClass()方法则直接通过如下代码完成方法的调用。

    +展开
    -C#
    MethodInfo mi = MyTp.GetMethod("Method");
    string txt = (string)mi.Invoke(MyObj, MyList); 


      首先第1行代码从Type类型中获取参数所指定名称的方法,并返回MethodInfo类型的对象mi,然后调用mi的Invoke()方法,即可执行Method()方法。Invoke()方法的第1个参数调用Method()方法的对象,第2个参数传递给方法的参数数组,如果Method()方法没有参数,可以使用null。

    解析

      由于没有在当前程序集的清单中列出动态加载的外部程序集,所以编译时编译器并不知道该程序集是否存在。如果在程序中创建该程序集指定类型的实例,并调用其成员,这就是晚期绑定技术。晚期绑定可以使用System.Activator类,如以下代码所示:

    +展开
    -C#
    using System;
    using System.Reflection;

    Assembly am = Assembly.Load("外部程序集名称");
    Type tp = am .GetType("MyClass",false,false);
    object obj = Activator.CreateInstance(tp);


      以上代码创建了MyClass类的对象引用obj,如果需要调用其成员,不能直接用点符号调用,因为该对象为object类型,而不是指定类型。调用其成员可以使用该类型Type对象的InvokeMember()方法,并根据多个参数确定所调用的成员信息。例如, obj对象的Method()方法接收string类型的一个参数,并且返回类型为string。如果调用obj对象的Method()方法,方法代码如下所示:

    +展开
    -C#
    string[] Param = new string[1] { "参数值" };
    string txt = (string)OldTp.InvokeMember(
    "Method",//成员名称
    BindingFlags.InvokeMethod,//绑定标记,
    null,//绑定对象,
    obj,//所调用对象
    Param//所传递参数数组
    );


      以上代码中的InvokeMember()方法的返回值即为Method()方法的返回值,由于InvokeMember()方法返回值是 object类型,所以需要强制转换为string类型再赋值给txt。InvokeMember()方法的第1个参数为字符串类型,代表所调用成员的名称;第2个参数为绑定标记,代表成员类型,该值需要访问BindingFlags类,例如成员方法BindingFlags.InvokeMethod;第3个参数为绑定对象,一般使用null,代表使用默认绑定器,即System.Type.DefaultBinder类对象;第4个参数为所调用的对象 obj,如果调用静态方法,该参数为null。第4个参数传递给方法参数数组,例如代码中的Param。

      创建外部程序集指定类型的实例也可调用当前应用程序域的CreateInstanceAndUnwrap()方法,如以下代码所示:

    +展开
    -C#

    object obj = AppDomain.CurrentDomain.
    CreateInstanceAndUnwrap("程序集名称""类型名称");



    面试例题10:如何通过晚期绑定读写属性和字段成员?

    考点:InvokeMember()方法读写属性和字段成员以及Invoke()方法读写属性和字段成员。

    出现频率:★★★

    解答

      晚期绑定读写属性和字段成员均可以使用晚期绑定对象的InvokeMember()方法实现,也可以使用所指定类型的Type对象反射实现。本题在 LateBindingOther.cs代码中,主程序读写Human.dll程序集中Person类的Name属性和_age字段。在目录下新建一个程序文件,并命名为Person.cs,编写代码如代码7.13所示。

    代码7.13 外部Person类:Person.cs

    +展开
    -C#
    using System;

    class Person
    {       
    //定义两个字段,其中_name字段由Name属性读写
    string _name;
    //定义public的int类型的_age字段
    public int _age = 0;
    public string Name
    {
    get
    {
    return _name;
    }
    set
    {
    _name = value;
    }
    }
    }


      在目录下新建一个程序文件,并命名为LateBindingOther.cs,编写代码如代码7.14所示。

    代码7.14 晚期绑定读写属性和字段成员:LateBindingOther.cs

    +展开
    -C#
    using System;
    //导入相应的命名空间
    using System.Reflection;
    using System.IO;

    class LateBindingOther
    {
    static void Main(string[] args)
    {
    try
    {
    //接收用户输入的两组值,并传递给ClassOP方法
    Console.Write("/n【1】请输入需写入Person的属性值:");
    string inputA = Console.ReadLine();
    Console.Write("/n【2】请输入需写入Person的字段值:");
    int inputB = System.Convert.ToInt32(Console.ReadLine());
    Console.WriteLine("/n/t=======以下是属
    性和字段被写入后读出的结果=======/n");
    ClassOP(inputA, inputB);
    }
    //捕获输入格式异常
    catch (FormatException e)
    {
    //输出异常信息
    Console.WriteLine("异常信息:{0}", e.Message);
    }
    //捕获文件未找到异常
    catch (FileNotFoundException e)
    {
    //输出异常信息
    Console.WriteLine("异常信息:{0}", e.Message);
    }
    //捕获一般异常
    catch (Exception e)
    {
    //输出异常信息
    Console.WriteLine("异常信息:{0}", e.Message);

    }
    static void ClassOP(string a, int b)
    {
    //调用Assembly的Load方法,载入Human程序集
    Assembly am = Assembly.Load("Human");
    //获取Person类的Type对象
    Type tp = am.GetType("Person"falsefalse);
    //调用Activator类的CreateInstance方法
    //该方法可创建参数类型的实例
    object obj = Activator.CreateInstance(tp);
    //写入Name属性值
    tp.InvokeMember("Name", BindingFlags.SetProperty, 
    null, obj, new string[] { a });
    //读取Name属性值
    string name = (string)tp.InvokeMember("Name"
    BindingFlags.GetProperty, null, obj, null);
    Console.WriteLine("Person类的Name属性值为:【{0}】", name);
    //写入_age字段值
    tp.InvokeMember("_age", BindingFlags.SetField,
    null, obj, new object[] { b });
    //读取_age字段值
    int age = (int)tp.InvokeMember("_age"
    BindingFlags.GetField, null, obj, null);
    Console.WriteLine("Person类的_age字段值为:【{0}】", age);
    }
    }


      在命令行下将Person.cs编译为Human.dll程序集,编译LateBindingOther.cs,执行LateBindingOther程序。程序将提示"【1】请输入需写入Person的属性值:"和"【2】请输入需写入Person的字段值:",分别输入"比尔"和"50",运行结果如图7.16所示。

    图7.16 晚期绑定读写属性和字段成员

      本程序采用InvokeMember()方法成功地读写了Person类的Name属性和_age字段,也可以使用另一种方法(Invoke()方法)完成相同的功能。

    解析

      类似于晚期绑定调用方法,读写属性和字段成员可以用相同的方法实现。假设指定类型的Type对象为tp,晚期绑定所创建该类型对象为obj,使用InvokeMember()方法读写string类型的属性成员如以下代码所示:

    +展开
    -C#
    //写入属性
    tp.InvokeMember("属性名称", BindingFlags.SetProperty, null, obj, 
    属性值数组(object类型));
    //读取属性值到txt变量
    string txt = (string)tp.InvokeMember("属性名称"
    BindingFlags.GetProperty, null, obj, null);
    以上代码通过编写不同的绑定标记,访问BindingFlags类的成员,
    设定obj对象成员的操作类型及方式。如果不用InvokeMember()方法,
    也可选用Invoke()方法解决,如以下代码所示:
    //获取指定属性的PropertyInfo类型对象p
    PropertyInfo p = tp.GetProperty("属性名称");
    //写入属性
    p.SetValue(obj,属性值数组,null);
    //读取属性值到txt变量
    string txt = (string)p.GetValue(obj,null);


      该方法更为简单,也比较直观,SetValue()方法和GetValue()方法最后一个参数为索引化属性值的索引值,如果不是索引化属性值则取null。

  • 相关阅读:
    015-面向对象
    017-错误和异常
    019-File
    020-OS
    021-模块
    022-标准库
    数据库目录
    数据库 概念详解
    MySQL 基础
    MySQL 数据库操作
  • 原文地址:https://www.cnblogs.com/cpcpc/p/2123069.html
Copyright © 2020-2023  润新知