反射简介—C#特性和反射
.NET编译器的任务之一就是为所有定义和引用的类型生成元数据描述。除了程序集中标准的元数据外,.NET平台还支持特定(attribute)把更多的元数据嵌入到程序集中。
.NET特性扩展了抽象的System.Attribute基类,.NET中有很多预定义的特性,例如:[DllImport]、[Obsolete]和[Serializable]等等。
看一个Obsolete使用的例子,Obsolete特性用来标记不用的类或成员,当其他程序试图访问该项的时候,将会得到一个警告或错误描述。
static class StringUtil
{
public static string ReverseString(string str)
{
Console.WriteLine("call reverseString");
return "";
} [Obsolete("this legacy method should not be used", true)]
public static string LegacyReverseString(string str)
{
Console.WriteLine("call legacyReverseString");
return "";
}
} class Program
{
static void Main(string[] args)
{
string str = StringUtil.LegacyReverseString("Hello World!");
Console.Read();
}
}
使用上面的代码,我们就会的到一个错误消息,提示我们不应该在使用这个方法了
当然,我们也可以通过代码来添加自定义特性,在开始自定义特性之前,我们需要知道以下概念。
自定义特性
在代码中,我们可以创建自定义的特性类型,但是这个类型一定要直接或间接从System.Attribute派生。下面我们就定义了一个TableAttribute特性:
[AttributeUsage(AttributeTargets.Class, Inherited=false, AllowMultiple=false)]
public class TableAttribute : Attribute
{
public TableAttribute()
{
} public TableAttribute(string tableName)
{
this.TableName = tableName;
} public string TableName { get; set; }
}
注意:对一个特性类名使用Attribute后缀是一个惯例。然而,当我们把特性添加到一个程序实体,可以选择省略Atrribute后缀。编译器会首先在System.Attribute的派生类中查找被添加的特性类。如果没有找到,那么编译器会添加 Attribute后缀继续查找。
例如:当我们在代码中使用的时候,特性表现为[Obsolete],但是实际上类型是ObsoleteAttribute,而不是代码中的Obsolete。当.NET Framework进行名称转换的时候,所有的.NET特性(包括自定义特性)都将加上一个Attribute标记的后缀。
AttributeUsage
在上面的自定义特性中,有下面一行代码,我们给自定义特性应用了一个AttributeUsage特性。
[AttributeUsage(AttributeTargets.Class, Inherited=false, AllowMultiple=false)]
AttributeUsage类是一个预定义特性类,通过这个特性,我们可以控制自定义特性的使用,它描述了一个定制特性如和被使用。
[Serializable]
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
[ComVisible(true)]
public sealed class AttributeUsageAttribute : Attribute
{
public AttributeUsageAttribute(AttributeTargets validOn); public bool AllowMultiple { get; set; } public bool Inherited { get; set; } public AttributeTargets ValidOn { get; }
}
通过代码可以看到,AttributeUsage有三个属性:
- ValidOn
从代码中可以看到ValidOn的类型为System.AttributeTargets, 而AttributeTargets本身是
一个枚举,这样就可以通过按位"或"运算组合 AttributeTargets,从而指示哪些程序元素是有效的。
例如:
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- AllowMultiple
这个属性标记了特性能否被重复放置在同一个程序实体前多次。
- Inherited
这个属性来控制定制特性的继承规则,表示特性能否被继承。
从上面的介绍可以看到,创建自定义特性的大概步骤:
- 声明特性类,由 System.Attribute 直接或间接派生
- 使用AttributeUsage特性来控制自定义特性
- 声明特性类构造函数
特性和反射结合
当我们使用特性的时候,只是给程序集添加了一些元数据。当结合反射使用的时候,特性就能发挥很大的作用了。
下面看一个特性和反射结合的例子,在例子中自定义了Table和Column特性,然后把这些特性应用到了我们的User类型上面;然后结合一个自定义的ORM类型,将对象的Inster()操作转换成SQL语句。
namespace AttributeTest
{
[AttributeUsage(AttributeTargets.Class, Inherited=false, AllowMultiple=false)]
public class TableAttribute : Attribute
{
public TableAttribute()
{
} public TableAttribute(string tableName)
{
this.TableName = tableName;
} public string TableName { get; set; }
} [AttributeUsage(AttributeTargets.Property, Inherited=false, AllowMultiple=false)]
public class ColumnAttribute:Attribute
{
public ColumnAttribute()
{
} public ColumnAttribute(string columnName)
{
this.ColumnName = columnName;
} public ColumnAttribute(string colunmName, DbType dbType)
{
this.ColumnName = colunmName;
this.DbType = dbType;
} public string ColumnName { get; set; }
public DbType DbType { get; set; }
} public class CustomizeORM
{
public void Insert(object table)
{
Type type = table.GetType();
Dictionary<string, string> columnValueDict = new Dictionary<string, string>();
StringBuilder SqlStr = new StringBuilder();
SqlStr.Append("insert into ");
TableAttribute tableAttribute = (TableAttribute)type.GetCustomAttributes(typeof(TableAttribute), false).First();
SqlStr.Append(tableAttribute.TableName);
SqlStr.Append("("); foreach (var prop in type.GetProperties())
{
foreach (var attr in prop.GetCustomAttributes())
{
string value = prop.GetValue(table).ToString();
ColumnAttribute columnAttribute = attr as ColumnAttribute;
if (columnAttribute != null)
{
columnValueDict.Add(columnAttribute.ColumnName, value);
}
}
} foreach (var item in columnValueDict)
{
SqlStr.Append(item.Key);
SqlStr.Append(","); }
SqlStr.Remove(SqlStr.Length - , );
SqlStr.Append(") values('");
foreach (var item in columnValueDict)
{
SqlStr.Append(item.Value);
SqlStr.Append("','");
}
SqlStr.Remove(SqlStr.Length - , );
SqlStr.Append(")");
Console.WriteLine(SqlStr.ToString());
}
} [Table("Users")]
public class User
{
[Column(ColumnName="Id",DbType=DbType.Int32)]
public int UserID{get;set;}
[Column(ColumnName="Name",DbType=DbType.String)]
public string UserName { get; set; }
[Column(ColumnName = "Age", DbType = DbType.Int32)]
public int Age { get; set; }
} class Program
{
static void Main(string[] args)
{
CustomizeORM customizeORM = new CustomizeORM();
User user = new User() { UserID = , UserName = "Wilber", Age = };
customizeORM.Insert(user); Console.Read();
}
} }
代码的执行结果为:
从这个例子中可以看到,通过特性给程序集加入的元数据,可以在运行时被反射程序得到并使用。
通过IL代码可以看到特性的简化,代码中我们使用Table、Column对User类型进行修饰,在IL代码中都加上了Attribute后缀;同时还可以看到,这些特性都变成了User类型的元数据。
总结
通过本文介绍了.NET特性,同时介绍了自定义特性需要的基本知识。
特性本身只是给程序集添加一些元数据,当结合反射使用的时候,这些被添加的元数据才能发挥更大的作用。
反射简介—C#特性和反射的更多相关文章
- Day07 jdk5.0新特性&Junit&反射
day07总结 今日内容 MyEclipse安装与使用 JUnit使用 泛型 1.5新特性 自动装箱拆箱 增强for 静态导入 可变参数方法 枚举 反射 MyEclipse安装与使用(yes) 安装M ...
- Java 反射(一)反射简介、原理和应用场景
目录 一.动态语言和动态语言的比较 动态语言 静态语言 二.反射 简介 反射的常见使用 1. 代码编辑器 2. Spring等框架的IoC容器 3. 和注解的配合使用 原理 反射优缺点 调试查看 Cl ...
- C#特性和反射
C#特性和反射 .NET编译器的任务之一就是为所有定义和引用的类型生成元数据描述.除了程序集中标准的元数据外,.NET平台还支持特定(attribute)把更多的元数据嵌入到程序集中. .NET特性扩 ...
- 利用特性和反射给泛型Model赋值
为了解决从数据库读取的表字段和自己建的viewModel字段名称不相符的问题 本人小白,初次将特性及反射应用到实例,写的不好的地方还请大家多多包涵 新建一个控制台应用程序命名为ReflectAndAt ...
- EF+LINQ事物处理 C# 使用NLog记录日志入门操作 ASP.NET MVC多语言 仿微软网站效果(转) 详解C#特性和反射(一) c# API接受图片文件以Base64格式上传图片 .NET读取json数据并绑定到对象
EF+LINQ事物处理 在使用EF的情况下,怎么进行事务的处理,来减少数据操作时的失误,比如重复插入数据等等这些问题,这都是经常会遇到的一些问题 但是如果是我有多个站点,然后存在同类型的角色去操作 ...
- 详解C#特性和反射(一)
使用特性(Attribute)可以将描述程序集的信息和描述程序集中任何类型和成员的信息添加到程序集的元数据和IL代码中,程序可以在运行时通过反射获取到这些信息: 一.通过直接或间接的继承自抽象类Sys ...
- 详解C#特性和反射(四)
本篇内容是特性和反射的最后一篇内容,前面三篇文章: 详解C#特性和反射(一) 详解C#特性和反射(二) 详解C#特性和反射(三) 一.晚期绑定(Late Binding)是一种在编译时不知道类型及其成 ...
- day27<反射&JDK5新特性>
反射(类的加载概述和加载时机) 反射(类加载器的概述和分类) 反射(反射概述) 反射(Class.forName()读取配置文件举例) 反射(通过反射获取带参构造方法并使用) 反射(通过反射获取成员变 ...
- 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码
详解C#泛型(二) 一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...
随机推荐
- NetBeans Lookups Explained!
https://dzone.com/articles/netbeans-lookups-explained —————————————————————————————————————————————— ...
- js实现选集功能
项目中有个播放列表选集的需求,如下图: 现在展示的1-42集全部,我们如何实现这个选集的功能呢? 我的思路如下: 1.将这42集按每10集划分,并存入数组: 2.保存开始和结束位置,比如说1~10,开 ...
- PHP产生随机数
PHP生成随机字符串包括大小写字母 PHP生成随机字符串包括大小写字母,这里介绍两种方法: 第一种:利用字符串函数操作 ? <?php /** *@blog <www.p ...
- Axiom3D:手动创建ManualObject与Mesh,以及如何使用Cg着色器语言
在开始正文前,先说下Axiom3D里遇到的二个BUG. 1.在启动axiom生成的程序中,我发现输出里总是有一些如"billboard_type","billboard_ ...
- (笔记)Mysql命令mysqldump:备份数据库
mysqldump命令用来备份数据库. mysqldump命令在DOS的[url=file://\\mysql\\bin]\\mysql\\bin[/url]目录下执行. 1) 导出整个数据库(导出文 ...
- e860. 列出组件绑定的键盘键
This example demonstrates how to list all the key bindings in a component. Text components have an a ...
- java Object类源代码详解 及native (转自 http://blog.csdn.net/sjw890821sjw/article/details/8058843)
package java.lang; public class Object { /* 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用.*/ private static na ...
- 详解Paste deploy
原创作品,转载注明本文出处:http://www.cnblogs.com/Security-Darren/p/4087587.html 谈到WSGI,就免不了要了解paste,其中paste depl ...
- 【经验之谈】Git使用之TortoiseGit配置VS详解;国内几大Git代码托管网站
转载自: http://www.cnblogs.com/xishuai/p/3590705.html http://www.cnblogs.com/shanyou/p/3662482.html
- vimdiff的常用命令
★ 跳转到下一个diff点: 请使用 ]c 命令★ 跳转到前一个diff点: 请使用 [c命令如果在命令前加上数字的话,可以跳过一个或数个差异点,从而实现跳的更远.比如如果在位于第一个差异点的行输入& ...