当前位置:网站首页>C#中Attribute(特性)
C#中Attribute(特性)
2022-06-29 09:26:00 【zlbcdn】
目录
更新
之前的关于C#中Attribute博客是在4年前的2018年1月9日发布的,当时是《CLR via C#》读书笔记。在实际工作中并未经常使用,因此基本上忘得差不多。前几天在学习NPOI时,又重新发现了这个Attribute挺有意思,所以再重新学习一下。
1、获取Attribute的设定值
Attribute是一系列设定值,所以设定完成后,一定要能够获取,这样设置了才有用。而使用最多的就是通过反射进行的。
1.1 使用反射获取设置值
下面是通过反射,实现了获取设定值
using System;
using System.Collections.Generic;
using System.Reflection;
namespace AttributeRefelectionConsole
{
//
class Program
{
static void Main(string[] args)
{
DataModel tempData=new DataModel(){
ItemID="1",ItemName="ZL"};
Program tempClass = new Program();
var listdata = tempClass.getAttributeNames<DataModel>(tempData);
listdata.ForEach(t => {
Console.WriteLine(t);
});
Console.Read();
}
//获取设定类的Attribute的List
public List<string> getAttributeNames<T>(T entity)
{
List<string> resultList = new List<string>();
PropertyInfo[] pi = entity.GetType().GetProperties();
foreach (var item in pi)
{
//GetCustomAttributes返回的是一个object的数组
var obj= item.GetCustomAttributes(typeof(DataSQLAttribute), false);
if (obj != null)
{
//因为在DataModel的ItemID和ItemName属性上,每个属性只应用了1个Attribute
//所以可以使用obj[0]这种写法。若在一个属性上有多个Attrbute,则需要进行判断
var displayname= ((DataSQLAttribute)obj[0]).GetDisplayName();
resultList.Add(displayname);
}
}
return resultList;
}
}
//定义的数据Model
public class DataModel
{
[DataSQLAttribute("第一个属性的特性设定值")]
public string ItemID {
get; set; }
[DataSQLAttribute("第二个属性的特性设定值")]
public string ItemName {
get; set; }
}
//自定义的特性值
public class DataSQLAttribute : Attribute
{
private string displayName;
public DataSQLAttribute(string displayName)
{
this.displayName = displayName;
}
public string GetDisplayName()
{
return displayName;
}
}
}
因为在DataModel的属性只使用了一个Attribute,因此在getAttributeNames方法中,直接使用了(DataSQLAttribute)obj[0]强制类型转换。若属性中有多个Attribute,则需要进行判断。判断的代码如下:
//获取设定类的Attribute的List
public List<string> getAttributeNames<T>(T entity)
{
List<string> resultList = new List<string>();
PropertyInfo[] pi = entity.GetType().GetProperties();
foreach (var item in pi)
{
object[] obj= item.GetCustomAttributes(typeof(DataSQLAttribute), false);
if (obj != null)
{
//方法2,若有多个Attribute,需要进行判断的
foreach (var objitem in obj)
{
//进行判断
if (objitem is DataSQLAttribute)
{
var displayname = ((DataSQLAttribute)obj[0]).GetDisplayName();
resultList.Add(displayname);
}
}
}
}
return resultList;
}
1.2 代码下载
1.3 使用Attribute实现ORM
可以使用Attribute实现一个ORM。代码如下:
//获取设定类的Attribute的List
public int AddPerson<T>(T entity)
{
string oracleDataTableName = "";
string[,] paraAndData = new string[3, 3];
Type zlt = entity.GetType();
//获取对应的表名
object[] tableObj = zlt.GetCustomAttributes(typeof(TableAttribute), false);
if(tableObj!=null)
{
oracleDataTableName= ((TableAttribute)tableObj[0]).getDataTableName();
}
//获取字段名称及属性值
PropertyInfo[] pi = zlt.GetProperties();
for (int i = 0; i < pi.Length; i++)
{
object[] obj = pi[i].GetCustomAttributes(typeof(FieldAttribute), false);
if (obj != null)
{
FieldAttribute temp = ((FieldAttribute)obj[0]);
paraAndData[i,0] = temp.GetDataColumnName();
paraAndData[i, 1] = temp.GetDataColumnType();
paraAndData[i, 2] = pi[i].GetValue(entity, null).ToString();
}
}
//形成SQL,这个可以进行自己处理
string sqlformat = @"insert into {0}({1},{2},{3}) values ('{4}','{5}',{6})";
string sql = string.Format(sqlformat, oracleDataTableName, paraAndData[0, 0], paraAndData[1, 0], paraAndData[2, 0], paraAndData[0, 2],
paraAndData[1, 2], paraAndData[2, 2]);
return 1;
}
形成的SQL如下:
1.3 代码下载
代码的下载如下:下载链接
1.3 使用Attribute获取设置值
可以直接使用Attribute类进行操作
using System;
using System.Reflection;
using System.Diagnostics;
[assembly:CLSCompliant(true)]
namespace NETAttribute
{
[Serializable]
[DefaultMember("Main")]
[DebuggerDisplay("Zheng",Name ="Lin",Target =typeof(Program))]
public class Program
{
[Conditional("Debug")]
[Conditional("Release")]
public void SomeMethod() {
}
public Program() {
}
[CLSCompliant(true)]
[STAThread]
public static void Main(string[] args) //此处Main方法需为public,否则Main不会暴露,[DefaultMember("Main")]将无法应用到Main上
{
ShowAttributes(typeof(Program));
MemberInfo[] members = typeof(Program).FindMembers(MemberTypes.Constructor | MemberTypes.Method,
BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static,
Type.FilterName, "*");
foreach (MemberInfo item in members)
{
ShowAttributes(item);
}
Console.Read();
}
//获取当前类型中的Attribute设定值
private static void ShowAttributes(MemberInfo attributeTarget)
{
//获取目标上的Attribute
Attribute[] attributes = Attribute.GetCustomAttributes(attributeTarget);
//展示目标信息
Console.WriteLine("当前目标的名称是{0},本目标上的Attribute有:{1}", attributeTarget.Name, (attributes.Length == 0 ? "无" : string.Empty));
//遍历Attributes并将信息展示
foreach (Attribute item in attributes)
{
//显示Attribute的类型
Console.WriteLine("Attribute的名称是{0}",item.GetType().ToString());
if (item is DefaultMemberAttribute)
Console.WriteLine("MemberName={0}",((DefaultMemberAttribute)item).MemberName);
if (item is ConditionalAttribute)
Console.WriteLine("ConditionString={0}",((ConditionalAttribute)item).ConditionString);
if (item is CLSCompliantAttribute)
Console.WriteLine("IsCLSCompliant={0}",((CLSCompliantAttribute)item).IsCompliant);
DebuggerDisplayAttribute dda = item as DebuggerDisplayAttribute;
if (dda != null)
{
Console.WriteLine("Value={0},Name={1},Target={2}", dda.Value, dda.Name, dda.Target);
}
}
Console.WriteLine();
}
}
}
2、Attribute使用过程中的注意点
2.1、Attribute的构造器的参数
Attribute是一个类,因此使用时,需要向其传递构造器必须的参数,以及初始化一些属性或设定。构造器的必须参数,被称之为“定位参数”(Positional Parameter),顺手初始化的属性或公共字段的参数,称之为“命名参数”(Named Parameter)。前者是必须,后者是选择项。在VS使用中,智能提醒如下所示。里面明确的指出,哪些是必填的定位参数,哪些是选填的命名参数。

2.2、使用注意事项
可以将多个Attribute用于单个目标元素。Attribute的先后顺序无所谓。多个Attribute可以放在一个中括号内,也可以多个。另外,若Attribute的构造器不需要参数,则Attribute后面的圆括弧也可以删除。举例,以下都是相同的:
[Serializable][Flags]
[Serializable,Flags]
[SerializableAttribute,FlagsAttribute]
[FlagsAttribute(), SerializableAttribute()]
2.3 常用的Attribute
(一)常用的Attribute有:
1、DllImport应用于方法,是指告诉CLR,方法的实现位于制定路径的非托管DLL中。
2、Flags应用于枚举类型。告诉CLR,将枚举类型作为一个位标志位集合使用
3、Serializable应用于类型。告诉序列格式化器,一个实例的字段可以序列化和反序列化。
…
具体详细的可以参见:MSDN上Attribute的介绍内容
(二)使用时的注意事项或语法
在Attribute使用前,需要有一些前缀。例如:[method:DllImport(“…”)],有时,前缀可以省略,编译器会自动判断。但有一些不能省略。以下是Attribute的语法。下面代码的带有“//前缀必须”代表不能删除该前缀
using System;
[assembly:SomeAttr] //前缀必须
[module:SomeAttr] //前缀必须
namespace NETAttribute
{
[type:SomeAttr]
class AttributeGrammar<[typevar:SomeAttr] T>
{
[field:SomeAttr]
public int SomeFiled = 0;
[return: SomeAttr] //前缀必须
[method: SomeAttr]
public int SomeMethod([param:SomeAttr]int someParam)
{
return SomeParam
}
[property: SomeAttr]
public string SomeProp
{
[method: SomeAttr]
get {
return null; }
}
[event: SomeAttr]
[field: SomeAttr] //前缀必须
[method: SomeAttr] //前缀必须
public event EventHandler SomeEvent;
}
}
2.4 自定义Attribute注意事项
自定义Attribute需要满足两个条件
1、必须继承system.Attribute抽象类
2、所有非抽象的Attribute至少包含一个公共构造器
另外,根据“行规”,自定义的Attribute要以“Attribute”字母结尾。
原则
Attribute的本质是一个类,但这个类只是提供一些基本的状态信息,或用作逻辑状态的一个容器。不应该在该类中实现复杂的方法、逻辑,更不应该提供方法、事件或者其他成员。若需要这些,请单独编写一个逻辑类(class文件)。不要把Attribute搞得复杂。
3、其他
重构ShowAttributes方法
//获取当前类型中的Attribute设定值
private static void ShowAttributesBuReflection(MemberInfo attributeTarget)
{
//获取目标上的Attribute
IList<CustomAttributeData> attributes = CustomAttributeData.GetCustomAttributes(attributeTarget);
//展示目标信息
Console.WriteLine("当前目标的名称是{0},本目标上的Attribute有:{1}", attributeTarget.Name, (attributes.Count == 0 ? "无" : string.Empty));
//遍历Attributes并将信息展示
foreach (CustomAttributeData item in attributes)
{
Type t = item.Constructor.DeclaringType;
Console.WriteLine("{0}", item.ToString());
Console.WriteLine("构造器={0}", item.Constructor);
IList<CustomAttributeTypedArgument> posArgs= item.ConstructorArguments;
Console.WriteLine("是否存在定位参数={0}",(posArgs.Count==0)?"否":string.Empty);
foreach (CustomAttributeTypedArgument m in posArgs)
{
Console.WriteLine("参数的类型:{0},参数值:{1}",m.ArgumentType,m.Value);
}
IList<CustomAttributeNamedArgument> namedArgs = item.NamedArguments;
Console.WriteLine("是否存在命名参数={0}", (namedArgs.Count == 0) ? "否" : string.Empty);
foreach (CustomAttributeNamedArgument m in namedArgs)
{
Console.WriteLine("参数名称:{0},参数的类型:{1},参数值:{2}", m.MemberInfo.Name,
m.TypedValue.ArgumentType, m.TypedValue.Value);
}
}
Console.WriteLine();
}
运行结果如下:
4、总结
这个Attribute还是比较好用。
边栏推荐
- We can't tell the difference between distributed and cluster. Let's tell the story of two cooks cooking
- 走迷宫 bfs 中等+——最后的编程挑战
- Codeforces - 1151b thinking
- Wandering --- 最后的编程挑战
- September 21, 2020 referer string segmentation boost gateway code organization level
- To 3 -- the last programming challenge
- 函数指针、函数指针数组、计算器+转移表等归纳总结
- mysql中的if [not] exists
- QGIS mapping
- 十六制计数器和流水灯
猜你喜欢

Seaweedfs security configuration

六度空间 bfs

September 29, 2020 non commodity templating code level rapidjson Library

PGP在加密技术中的应用

Nacos registry cluster

QGIS mapping

Web漏洞手动检测分析

Slide the custom control to close the activity control

Installing and configuring wmware esxi 6.5.0 in VMware Workstation

Download control 1 of custom control (downloadview1)
随机推荐
manacher
逆向思维-小故事
L2-031 深入虎穴 (25 分)
2019.10.27 training summary
1147 heaps (30 points)
Weight recursion of complete binary tree -- the last programming challenge
manacher
F5 big IP Icontrol rest command execution (cve-2022-1388)
Codeforces Round #659 (Div. 2)
云主机端口扫描
信号作品:时变和时不变
2019.11.13 training summary
Power strings [KMP cycle section]
L2-025 分而治之 (25 分)
任务调度器之Azkaban的使用
C语言库函数--strstr()
L2-026 small generation (25 points)
Ural1517 freedom of choice [suffix array: longest common continuous substring]
L2-026 小字辈 (25 分)
1146 topological order (25 points)