android 集成同一interface不同泛型_C# 基础知识系列- 10 反射和泛型(二)

0. 前言

这篇文章延续《C# 基础知识系列- 5 反射和泛型》,继续介绍C#在反射所开发的功能和做的努力。上一篇文章大概介绍了一下泛型和反射的一些基本内容,主要是通过获取对象的类型,然后通过这个类型对象操作对象。这一篇介绍一个在反射中很重要的内容:特性,以及上一篇未完成的内容——泛型在反射中的引用。

1. 特性

特性是一种类增强技术,配合解析对应的解析方法可以完成很多类原本没有的功能。特性本质是一种标签,可以标注在类、方法、属性等。它是类本身的一种信息扩展,就像生活中一个人只有一个身份证号,但是可以有多个身份一样,而这些多出来的身份对于类来说就是特性。特性虽然是对类的增强,但不局限于在类上做标记,属性、方法上都可以。

在C#中特性分为三种,位映射特性、自定义特性和伪自定义特性。

位映射特性,举个例子,在C#中一个类会有public、private、abstract(抽象类)、saled(不能继承)等修饰符,而这些修饰符在C#编译的过程中会生成一串二进制码,里面存放就是 是否是public、是否是private 等。这些就是位映射特性的一部分,位映射特性对我们来说是无法进行扩展和修改的,所以就不做更多的介绍。

我们通常说的特性一般指的是自定义特性,这部分特性也是我们能够扩展的,也是我们实际开发中用的特性。

1.1 定义一个特性

如何正确的定义一个特性呢?在C#中,特性也是类的一种。所以声明一个特性,就如同声明一个类一样,不同的是,这个类指定一个根父类是System.Attribute。所有自定义特性都是这个类的子类或者后代类,无一例外。同时,C#提倡在定义一个特性类的时候,类名应当以Attribute结尾,在使用的时候可以自动忽略。

示例:

public class DemoAttribute : Attribute {   }

以上实例就是定义了一个很普通的特性类,用了也没有任何用的特性。因为特性只是一种标签。这个特性类可以用在任何支持特性的地方,当这个特性标记一个类的时候,目标类的子类也将自动获取这个特性。

以上是一个特性的默认行为,如果我们想要对此做一定限制的话,那么就需要用到特性System.AttributeUsageAttribute。这个特性类用来控制特性的使用方式。

public bool Inherited { get; set; }// 该特性是否可以被子类继承,默认是 True
public bool AllowMultiple { get; set; }// 一个类是否可以多次使用该特性做标记,默认是 False
public AttributeTargets ValidOn { get; }//获取一组值,这组值标识指示的属性可应用到的程序元素,该参数使用构造方法赋值

我们再来看看AttributeTargets里有些什么吧。

[System.Flags]
public enum AttributeTargets
{Assembly = 1,// 表示特性是用在 Assembly上的,不常用Module = 2, //特性是用在 Module上的,不常用Class = 4,  // 表示特性是用来类上的Struct = 8, //表示用在结构体上Enum = 16, // 0x00000010   表示用在枚举上Constructor = 32, // 0x00000020   构造方法Method = 64, // 0x00000040       普通方法Property = 128, // 0x00000080     属性Field = 256, // 0x00000100        字段Event = 512, // 0x00000200         事件Interface = 1024, // 0x00000400    接口Parameter = 2048, // 0x00000800    方法的参数Delegate = 4096, // 0x00001000     委托ReturnValue = 8192, // 0x00002000   返回值GenericParameter = 16384, // 0x00004000   泛型参数All = GenericParameter | ReturnValue | Delegate | Parameter | Interface | Event | Field | Property | Method | Constructor | Enum | Struct | Class | Module | Assembly, // 0x00007FFF ,所有
}

我们常用的限制是ALL或者类等,限制也可以是多个,写法如下:限制A|限制B|限制C,表示A、B、C三种限制共存。具体原理是因为 AttributeTargets 是支持位运算的枚举,通过一定的位运算可以在一个值中间存放多个枚举。

说了这么多,我们自己重新写一个特性类吧:

1.限定只能给类使用的特性

[AttributeUsage(AttributeTargets.Class)]
public class DemoAttribute : Attribute { }

2.限定只能给方法使用的特性

[AttributeUsage(AttributeTargets.Method)]
public class DemoAttribute : Attribute
{
}

3.限定不能继承的特性

[AttributeUsage(AttributeTargets.All, Inherited = false)]
public class DemoAttribute : Attribute
{
}

4.限定类和枚举可以使用,但不能继承的 特性

[AttributeUsage(AttributeTargets.Class| AttributeTargets.Enum, Inherited = false)]
public class DemoAttribute : Attribute
{
}

需要注意的一点就是,如果要指定是否可以被继承或者是否允许多次使用 这两个属性则需要先指定特性的作用范围,即限定是类能使用还是所有都可以。

1.2 使用特性

我们自定义了一个特性,就必须使用它才能会有意义,否则它只是一个普通的类。那么我们该如何使用呢?其实在上一节中我们隐晦的介绍了特性的使用方式。就是用中括号包裹起来,给类、属性、方法等标记起来。

首先我们定义一个贼普通的特性:

public class DemoAttribute : Attribute {   }//没有任何限制,可以用在任何支持特性的地方

然后使用它:

[Demo]//[DemoAttribute]
public class TestDemo
{    
}

如示例所示,在类上面添加[Demo]标记,表示这个类应用了特性DemoAttribute,也可以使用类名,但是C#会自动忽略类名中结尾的Attribute。当然有的人会把特性写在类或者方法等声明的同一行开头位置,不过我一般会写在不同行,毕竟阅读上简单明了。

我们之前说过,抛开它集成自Attribute类不提,它也是一个类。既然是类,那么就会有属性。那么现在定义一个带属性的特性类:

[AttributeUsage(AttributeTargets.Class| AttributeTargets.Enum, Inherited = false)]
public class DemoAttribute : Attribute
{public string Name { get; set; }
}

该特性声明了一个变量,使用方式如下:

[Demo(Name = "测试")]
public class Student{ }

DemoAttribute是一个只能用在 类、枚举 上的特性,有一个属性是Name。在使用的时候可以用(属性名="属性值")的方式为属性赋值。

更多的使用方式:

[AttributeUsage(AttributeTargets.Class| AttributeTargets.Enum, Inherited = false)]
public class DemoAttribute : Attribute
{public string Name { get; set; }public int Age { get; set; }public DemoAttribute(int age){Age = age;}
}

使用:

[Demo(10,Name = "测试")]
public Student(){ }

如果特性类声明了构造方法,那么在使用的时候,优先按照构造方法的顺序进行赋值,然后使用属性名=属性值的方式为其他属性进行赋值。

2. 特性在反射中的应用

在第一节中介绍了如何声明一个特性和使用特性,但是没有反射或者类加载技术,那么特性的作用就并没有想象中的那么大。就像人有多个身份,但是也得有对应的公司或者对应的环境。比如说,王XX有个身份是某XX公司老总,那么XX公司得需要在工商局注册登记,他这个身份才会有效。如果没有登记,那么这个身份也就是个虚名。当特性离开了反射,离开了类加载技术,特性就是摆设。当然这部分只限于自定义特性,因为C#内置的一些特性涉及到另外的技术:动态编译,或者需要编译器的配合。我们自定义的特性显然没有这些特权,所以必须我们手动开发对应的行为和规范。

首先,声明一个类和特性:

[AttributeUsage(AttributeTargets.Class| AttributeTargets.Enum, Inherited = false)]
public class DemoAttribute : Attribute
{public string Name { get; set; }public int Age { get; set; }public DemoAttribute(int age){Age = age;}
}
[Demo(10,Name = "测试")]
public class Student
{
}

2.1 获取类的特性

var stuType = typeof(Student);

上述代码先获取到一个类的类型对象,然后调用:

IEnumerable<CustomAttributeData> attrs = stuType.CustomAttributes;

将获取到这个类上声明的所有的自定义的特性,不过获取到的是一个CustomAttributeData,这个类封装了一个特性的特征,但是在我们使用起来会很困难,而且我们更多的需要得到特性本身的对象,而不是这种需要我们进一步处理的对象。那么,调用:

IEnumerable<Attribute> data = stuType.GetCustomAttributes(typeof(DemoAttribute));

通过上述方法就可以获取到一组类型是DemoAttribute的特性对象。

那么回想一下为什么是一组?在AttributeUsageAttribute有一个AllowMultiple属性,这个属性就是用来标记这个特性是否可以标注多个,也就是在同一目标上多次使用,如果这个值为True,则在此处将获取不定个,否则最多一个。具体取决于对目标做了多少标记。

获取到特性之后,依据实际需求进行开发。这里就不做过多介绍了,在后续篇幅中会对这部分的使用做更多的介绍。

2.2 获取其他元素的特性

特性不止可以标记在类上,还可以标记在属性、方法上。那么这些元素应该如何获取对应的特性呢?

1. 属性

var stuType = typeof(Student);
var property = stuType.GetProperties()[0];//假设类有一个Property
var attrs = property.GetCustomAttributes(typeof(DemoAttribute));

2. 方法

var stuType = typeof(Student);
var method = stuType.GetMethods()[0];// 假设类有一个方法
var attrs = method.GetCustomAttributes(typeof(DemoAttribute));

需要注意的地方是,var 关键字;DemoAttribute只是一个代指,不是特指之前声明的DemoAttribute特性类,因为之前声明的特性类没有对属性和方法进行支持,所以在本节中直接使用会编译不通过。

特性就先简单的介绍到这里,特性可以标记给很多目标比如程序集、模块、类等一系列,但实际开发至少是Web开发中,更多的是标记类、方法、属性等。这里只是介绍了特性的声明和使用,但是没有介绍实际开发中特性的使用,这部分有机会在后续篇幅中介绍吧。因为我也用的不是很多。

3. 反射中的泛型

之前在《C# 基础知识系列- 5 泛型和反射》介绍过,C#的泛型不会在编译过程中抹去痕迹,意思就是我们可以通过反射获取到对象的实际泛型类型。那么如何获取呢?

var stuType = typeof(Student);
// 获取类的泛型参数
var genericTypes = stuType.GenericTypeArguments;var method = stuType.GetMethods()[0];
// 获取方法的泛型参数
var types = method.GetGenericArguments();

这个问题,在我写Java代码的时候,困扰了我很久,没有很好的办法。但是在C#中,我可以不用考虑这个问题。

4. 总结

反射在各大编程语言中是一个很重要的特点,泛型、特性在泛型中扮演着很重要的角色。反射在实际开发中扮演着很重要的角色,但是我们在开发中必须慎重考虑反射的使用。

到目前为止,反射介绍告一段落,但这不是结束。因为反射是个可深可浅的内容,目前只是介绍了依稀概念和理论上的一些内容,而更多的则隐藏在实际开发中,这时候就需要结合需求进行设计和代码编写了。

更多内容烦请关注我的博客《高先生小屋》

f1888a1bcb216fef77d5326c8a3b091a.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/573788.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

hdu 1297 递推难题

这题的话&#xff0c;我能玩一年 今天做了很多递推的题&#xff0c;这题无疑是最复杂的 其实可以看出来,2,3,4,5为一类&#xff0c;不妨定义为2型&#xff0c;1&#xff0c;6为一类&#xff0c;定义为1型 规定num[i]为结尾是i的凹槽的数量 我们可以能轻易的推出 sum num[1]*2n…

mysql 8.0远程连接_安装mysql 8.0.17并配置远程访问的方法

一、安装前准备查看数据库版本命令&#xff1a; mysql --versionmysql-community-common-8.0.17-1.el7.x86_64.rpmmysql-community-libs-8.0.17-1.el7.x86_64.rpmmysql-community-client-8.0.17-1.el7.x86_64.rpmmysql-community-server-8.0.17-1.el7.x86_64.rpm二、安装RPM包依…

python体育竞技分析代码200行_使用Python进行体育竞技分析(预测球队成绩)

使用Python进行体育竞技分析&#xff08;预测球队成绩&#xff09; 发布时间&#xff1a;2020-09-18 06:38:27 来源&#xff1a;脚本之家 阅读&#xff1a;69 今天我们用python进行体育竞技分析&#xff0c;预测球队成绩 一. 体育竞技分析的IPO模式 &#xff1a; 输入I(input)&…

为什么有时优盘是只读模式_JS专题之严格模式

ECMAScript 5 引入了 strict mode ,现在已经被大多浏览器实现&#xff08;从IE10开始&#xff09;一、什么是严格模式顾名思义&#xff0c;JavaScript 严格模式就是让 JS 代码以更严格的模式执行&#xff0c;不允许可能会引发错误的代码执行。在正常模式下静默失败的代码&#…

mysql router 介绍_MySQL Router 介绍篇

MySQL Router 是什么&#xff1f;相信还有很多人没有听说过MySQL Router&#xff0c;很多人对它还不了解&#xff0c;在这篇文章里&#xff0c;将对MySQL Router进行一个简明介绍。首先&#xff0c;介绍一下MySQL Router推出的背景。MySQL Router 是一个轻量级的中间件&#xf…

react 更新input 默认值setfieldsvalue_值得收藏的React面试题

react1、什么是虚拟DOM&#xff1f;难度: ⭐虚拟 DOM (VDOM)是真实 DOM 在内存中的表示。UI 的表示形式保存在内存中&#xff0c;并与实际的 DOM 同步。这是一个发生在渲染函数被调用和元素在屏幕上显示之间的步骤&#xff0c;整个过程被称为调和。2、类组件和函数组件之间的区…

实验二Step1-有序顺序表

1 #include<stdio.h>2 3 struct job4 {5 char name[10];//作业名称6 char status;//当前状态7 int arrtime;//到达时间8 int reqtime;//要求服务时间9 int startime;//调度时间 10 int finitme;//完成时间 11 float TAtime,TAWtime;//周转时…

ocx控件 postmessage消息会消失_APP控件之二——弹框

弹框分为两种&#xff1a;模态弹框和非模态弹框一、模态弹框模态弹框和非模态弹框最大的区别就是是否强制用户交互。模态弹框会打断用户的当前操作流程&#xff0c;用户不在弹框上操作的话&#xff0c;其余功能都使用不了。优点是&#xff1a;可以很好的获取的用户的视觉焦点缺…

结对编程(1)

我的结对编程项目搭档是王以正&#xff0c;我们的代码也是基于他个人项目的代码修改的。 由于王以正同学不在宿舍住也不怎么会宿舍&#xff0c;我们结对编程的时间较少&#xff0c;不过他将他的代码代码放到了github上面&#xff0c;这也让我有机会学习了github的使用。感觉这个…

伪代码block转换成程序流程图_程序设计基础

1、程序与程序设计语言的基本知识1&#xff09;程序&#xff1a;为解决某一问题而采用程序设计语言编写的一个指令集合。程序算法&#xff08;对操作的描述&#xff09;数据结构&#xff08;对数据的描述&#xff09;程序设计语言语言工具和环境。2&#xff09;程序的特点&…

mysql 内联函数_C++之内联函数

C继承C的一个重要特性是效率&#xff0c;在C中保护效率的一个方法是使用宏(macro),宏的实现是使用预处理器而不是编译器&#xff0c;预处理器直接用宏代码替换宏调用&#xff0c;所以就没有了参数压栈、生成汇编语言的CALL、返回参数、执行汇编语言的RETURN的时间花费&#xff…

10桌面管理文件收纳_二十余件精选桌面好物推荐,让学习工作生活满满正能量!...

这些提升办公桌幸福感的好物&#xff0c;能让你的学习与工作正能量满满&#xff01;01 笔记本支架笔记本是为了人们出行方便而设计的&#xff0c;显示器的位置并没有照顾到长期对着显示器码字的人群&#xff0c;许多以笔记本为主力的办公族也被迫成为了低头族。绿巨能(llano)笔…

hash 值重复_面试题:HashSet是如何保证元素不重复的

面试官&#xff1a;你能简单介绍List和Set有什么区别吗&#xff1f;小憨&#xff1a;List是一个有序的集合&#xff0c;在内存是连续存储的&#xff0c;可以存储重复的元素&#xff0c;List查询快&#xff0c;增删慢&#xff1b;Set是一个无序的集合&#xff0c;在内存中不连续…

RabbitMQ 原文译03--发布和订阅

发布/订阅 在之前的案例中我们创建了一个工作队列,这个工作队列的实现思想就是一个把每一个任务平均分配给每一个执行者,在这个篇文章我们会做一些不一样的东西,把一个消息发送给多个消费者,这种模式就被称作"发布/订阅". 为了说明这个模式,我们将要创建一个简单的日…

html富文本编辑器插件_vue中使用vuequilleditor富文本编辑器

点击上方“小姚同学技术栈”快速关注我哟&#xff01;vue-quill-editor是一个基于quill、适用于vue的富文本编辑器开源项目&#xff0c;支持服务端渲染和单页应用。目前项目热度还算可以&#xff0c;如果不考虑使用markdown&#xff0c;vue-quill-editor是一个比较好的选择。本…

二元函数图像生成器_GAN生成图像综述

点击上方“CVer”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达作者&#xff1a;YTimo(PKU EECS) 研究方向&#xff1a;深度学习&#xff0c;计算机视觉本文转载自&#xff1a;SIGAI摘要生成对抗网络(Generative adversarial network, GAN)…

设计模式之禅读书笔记

》设计原则《 》Single Responsibility Principle&#xff08;单一职责原则&#xff09;类只有一个修改的原因。 ●类的复杂性降低&#xff0c;实现什么职责都有明确的定义。 ●可读性高 ●可维护性高 ●变更引起的风险降低。 PS&#xff1a;基本不可能实现 》里氏替换原则&…

mysql mysql_set_charset_SQL注入攻击之 mysql_set_charset [转]

本文转载地址&#xff1a;http://hi.baidu.com/cuttinger/blog/item/e9a93901934755147bec2cb0.html1。老话题&#xff0c;mysql_real_escape_string单引号&#xff0c;大多数情况下&#xff0c;防止sql注入攻击足够了。$mysql mysql_connect("host","user&quo…

idea导入maven项目依赖报错_解决Maven依赖冲突的好帮手,这款IDEA插件了解一下?

1、何为依赖冲突Maven是个很好用的依赖管理工具&#xff0c;但是再好的东西也不是完美的。Maven的依赖机制会导致Jar包的冲突。举个例子&#xff0c;现在你的项目中&#xff0c;使用了两个Jar包&#xff0c;分别是A和B。现在A需要依赖另一个Jar包C&#xff0c;B也需要依赖C。但…

java线程创建方式_Java创建线程安全的方法

原文链接 译者&#xff1a;秦建平 校对&#xff1a;方腾飞首先来看一个问题&#xff1a;下面这个方法是线程安全的吗&#xff1f;如何才能让这个方法变成线程安全的&#xff1f;public class MyCount {private static int counter 0;public static int getCount(){return coun…