C#锐利体验 第十六讲 映射

C#锐利体验

南京邮电学院 李建忠(lijianzhong@263.net.cn

第十六讲 映射
动态类型查询
我们知道,C#编译后的PE文件主要由IL代码和元数据组成,元数据为.NET组件提供了丰富的自描述特性,它使得我们可以在代码运行时获知组件中的类型等重要的信息。在C#中这是通过一种称作映射(Reflection)的机制来完成的。先来看一个示例,我们首先创建一个简单的类型:
// SimpleType.cs
public class MyClass
{
    private int count=100;
    public int Count
    {    get{    return count;   }
        set{    count=value;    }
    }
    public void Print()
    {    System.Console.WriteLine(count);}
}
       
用编译命令csc /t:library SimpleType.cs编译上面的文件得到SimpleType.dll输出。我们再来实现查询类型的测试程序:
//Test.cs
using System;
using System.Reflection;
class Test
{
    public static void Main (string[] args)
    {
        Type t = typeof(MyClass);//
获取MyClass的类型信息
        Console.WriteLine("The Type Name : {0}",t.Name);//
获取类型的名字

        FieldInfo[] fiArr=t.GetFields();//
获取所有的共有域
        Console.Write("The {0} Fields :",fiArr.Length);
        foreach(FieldInfo o in fiArr)
        {
            Console.Write(o.Name+" ");
        }
        Console.WriteLine();
        PropertyInfo[] piArr=t.GetProperties();//
获取所有的公有属性
        Console.Write("The {0} Properties :",piArr.Length);
        foreach(PropertyInfo o in piArr)
        {
            Console.Write(o.Name+" ");
        }
        Console.WriteLine();
        MethodInfo[] miArr=t.GetMethods();//
获取所有的公有方法
        Console.Write("The {0} Methods :",miArr.Length);
        foreach(MethodInfo o in miArr)
        {
            Console.Write(o.Name+" ");
        }
    }
}
       
用编译命令csc /r:simpletype.dll test.cs编译后,执行可得到下面的输出:
The Type Name : MyClass
The 0 Fields :
The 1 Properties :Count
The 7 Methods :GetHashCode Equals ToString get_Count set_Count Print GetType
       
在上面的例子中,我们首先通过 typeof(MyClass)获得类MyClass的类型信息,当然我们也可以通过创建对象实例,然后调用对象实例的GetType方法来获得(每个类都从object根类中继承获得此方法)。在拥有了类型信息(变量t)后,我们便可以获得其类型的名字,该类型含有的公有域,公有属性,公有方法。注意这里C#的映射机制只允许我们获取类型的公有信息,这符合面向对象的封装的原则。这也是为什么我们虽然我们实现了count域,查询类型得到的输出却是“The 0 Fields :”——如果将SimpleType.cs中的count域改为public公有,我们将会得到他的查询信息。其中4个方法(GetHashCode Equals ToString GetType)都是继承自object类的公有方法,而方法get_Count set_Count则是我们在实现Count属性的副产物”——这符合我们前面讲述的属性本质上为方法的变体。实际上,System.Type类各种各样的成员使得我们能够获得几乎所有的与类型相关的公有信息。在System.Reflection命名空间下的各个类更是可以获得各个编程元素更较详细的信息,如方法的参数与返回值,域的类型,枚举的各个值等。

动态创建与调用
       
实际上映射还远远不止于动态地获知组件的类型信息,它还能使我们在获得类型信息的基础上,在代码运行时进行类型的动态创建与方法的动态调用,甚至于动态地创建并执行IL代码!
动态调用为C#的组件提供了晚绑定功能,它使得组件之间在运行时的集成变得极为方便!利用前面创建的简单的组件SimpleType.dll,我们来看一看怎样完成对象的动态创建和方法的动态调用:
// DynamicExe.cs
using System;
using System.Reflection;
class Test
{
    public static void Main ()
    {
        Assembly a=Assembly.LoadFrom("SimpleType.dll");//
装载组件

        foreach(Type t in a.GetTypes())
        {
            if(t.IsClass && !t.IsAbstract)
            {
                MethodInfo[] miArr=t.GetMethods();//
获得类型的公有方法
                    
                object o=Activator.CreateInstance(t);//
创建实例(无参构造器)        
                foreach(MethodInfo mi in miArr)
                {
                    if(!mi.IsAbstract && !mi.IsStatic
                        && mi.GetParameters().Length==0)
                    {
                        object re=mi.Invoke(o,null);//
调用实例方法
                        Console.WriteLine("{0}  , Return :{1}",mi.Name,re);
                    }
                }
            }
        }
    }
}
用编译命令csc /r:simpletype.dll dynamicexe.cs编译后,执行可得到下面的输出:
GetHashCode  , Return :8
ToString  , Return :MyClass
get_Count  , Return :100
100
Print  , Return :
GetType  , Return :MyClass
       
我们在上面的例子给出了被动态调用的方法的名字和返回值。其中输出的第4行为100,它是我们动态调用方法MyClass.Print() 的输出。需要指出的是我们通过一定的控制,仅仅调用的是类型的公有的无参数的实例方法。给出组件的名字,运用Assembly.LoadFrom我们便可以动态的装载组件。Activator.CreateInstance允许动态地创建类型(我们这里只通过无参的构造器来创建),实际上用它创建出来的类型和我们用MyClass o=new MyClass()创建出来的类型一模一样。进而,我们便可以在查询到的成员的基础上,对它们进行动态调用。
       Microsoft.NET
从底层的元数据设计入手,为映射机制提供了非常坚实的基础。命名空间System.ReflectionSystem.Reflection.Emit为操作这种映射提供了实实在在的强大的API编程接口,大大改善了组件的设计环境,提高了组件的交互能力!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值