C#动态编程

元数据是在程序编译过程中创建并嵌入到程序集中,利用反射技术可以读取程序的元数据,可以做以下操作:

  • 获取类型的成员,包含变量和函数
  • 实例化一个对象
  • 执行对象的成员函数
  • 获取类型的信息
  • 获取程序集的信息
  • 获取自定义特征
  • 创建和编译新程序集

自定义特征

预定义的许多特征,C#编译器都是支持的,也就是说,编译器可以在编译阶段识别并解读这些特征,然后根据这些特征的预定义规则去定制编译过程。而用户自定义的特征,编译器明显是不能识别的,不过编译器会将其作为元数据加在对应的元素上,嵌入到程序集中。特征转化为元数据后,我们就可以利用反射读取这些元数据,使程序在运行期间做出决策。说的通俗一点,自定义特征可能影响程序的逻辑走向。


//自定义一个特征,名称为FieldNameAttribute,可以作用于属性元素上,有一个必填的属性为name

//AttributeTargets是个枚举值,表示特征能用于什么元素上,这里设置为可以用于属性和字段上
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class FieldNameAttribute:Attribute{
    public string Name {get; private set;};
    public string Scoped {get;set;}
    public FieldNameAttribute(string name){
        this.Name = name;
    }
}

public class Person
{
    /*
    将FeildNameAttribute加载Name属性项,
    因为FieldNameAttribute的构造函数中有name参数,
    所以这里的“名字”,并没有加上name="...",而是直接赋的值
    */
    [FeildName("名字",Scpoed ="可选项")]
    public string Name { get; set; }

    public Person()
    {
    }

    public void foo(){
        Attribute info = Attribute.GetCustomAttribute(typeof(Person),typeof(FeildNameAttribute));
        FeildNameAttribute attr = (FeildNameAttribute)info;
        Console.WriteLine(attr.Name);
        Console.WriteLine(attr.Scoped);
    }
}

特征类本身会用一个预定义特征(System.AttributeUsage)来标记它是一个特征。特征类有了这个AttributeUsage特征后,编译器就会为它做特殊处理。

反射

上面讲的自定义特征,仅仅是为了给反射铺路。提到反射就必须要知道System.Type类。Type是访问元数据的主要方式,使用Type提供的方法可以获得指定类型的信息,比如获取一个类定义的构造函数,方法,属性,字段,事件等信息,甚至还以获得这个类依赖的模块和程序集等。可以使用Type描述的类型有:Class,Value type,Array,Interface,Enumeration,Delegate,generic type(由泛型创建的对象)。获取一个Type对象有以下3种方法:

  • typeof()运算符,typeof运算符中加要获取的类型的类型名,如:typeof(string)
  • Object.GetType(),这个方法是通过实例对象获取该对象的Type类型,如 var a = 0; a.GetType();
  • Type.GetType(),这个是Type类型提供的静态方法,Type type = Type.GetType("Demo.Person");

这里需要注意的是Type是abstract类型的,所以不能被实例化,我们通过上面的方式获得的Type实例,其实是程序在运行时根据具体的类型生成的Type派生类的实例。

Type实例获取的反射数据大致分为两类,第一类是描述类型本身的属性,比如类型的名称Name,类型的命名空间Namespace,完全限定名FullName等。第二类是类型内的元素,包括构造函数,方法,属性,字段等信息。

返回的对象类型 方法 描述
MemberInfo GetMember() 获取成员,包括属性,字段,方法,事件等
ConstructorInfo GetConstructor() 构造函数
MethodInfo GetMethod() 方法
EnventInfo GetEvnet() 事件
PropertyInfo GetProperty() 属性
FieldInfo GetField() 字段

以上介绍的都是使用反射读取类型的元数据,有了这些元数据后就可以实例化对象并调用其方法,以下是一些调用思路。

//获取Foo的公共无参构造函数,并实例化该对象
var foo = typeof(Foo).GetConstructors()
                    .ToList()
                    .Where(c => c.IsPublic && c.GetParameters().Length==0)
                    .First()?.Invoke(null);

//上面的列子可以进一步泛化,创建T类型的无参构造实例

/// <summary>
/// 创建T类型的无参构造实例
/// </summary>
public T CreateInstance<T>(){
    var result = typeof(T).GetConstructors()
                         .ToList()
                         .Where(c => c.IsPublic && c.GetParameters().Length == 0)
                         .First()?.Invoke(null);
    return (T)result;
}

//如果要创建有参的实例也是可以的,只是要稍微麻烦一点,需要获取指定与参数列表中参数类型和个数相同的构造函数,这里代码未写
//获取foo的方法并调用
typeof(Foo).GetMethod("Bar").Invoke(foo, new object[] { /* params */ });

动态类型

动态编程的实质是程序在编译时,不告诉编译器要创建什么类型的实例,等到程序运行时,动态的创建特定类型的实例和调用该实例的方法。

dynamic类型使编译器在编译期间忽略类型检查,dynamic对象与var关键字不同,var是编译器在编译期间反推对象的类型,也就是说,用var关键字定义的对象时,编译后会有明确的数据类型的,而dynamic对象编译后不知道是什么类型,它可以在运行期间任意的改变类型。

var a = 1;
a = "Hello World";//编译错误,a已经是int类型。

dynamic b = 1;
b = "Hello World"; //程序在运行时,动态的将b的类型修改为string类型
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容