特性是fw1.0提出的,是原始一种语法。
什么是特性?
在类或方法或属性上使用的,形如:
[Obsolete("这是一个提示信息")]// 内置特性
class Student
{
// 自定义特性
[NonEmpty]
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
使用的,叫做特性,特性是一个继承了Attribute抽象类的一个子类,这个类就是特性类,可以在特定的地方使用。
特性是一个类,可以用来标记元素,编译时生成medata,平时不影响程序的运行,除非主动的用反射查找,可以得到一些额外的信息/操作,然后扩展丰富的空间。
特性特点
特性可以影响程序的运行,特性是直接或间接继承attribute的子类一般声明特性的类的时候用Attribute结尾,而使用的时候可以省略。特性在默认情况下是不能重复修饰的。特性在编译后是metadata(元数据),所以只有反射可以使用。特性在声明之后是没用的,在程序运行过程中是没有影响的,除非用反射去找他,他才会使用。
修饰特性的特性
在.net Framwork框架中,内置了一个可以修饰特性的特性:
[AttributeUsage(AttributeTargets.All,AllowMultiple = false,Inherited = true)]
class NonEmptyAttribute:Attribute
{
}
AttributeUsage这个特性就是用来修饰特性的,其中有三个字段,分别代表不同的意义:
AttributeTargets //设置特性使用的目标,如在类上、字段上、属性上等都能,是一个枚举类型,如果为All则代表在所有类型上都可以使用。
AllowMultiple //代表是否可以重复标记,默认是不可以重复标记的,因为没有必要。为什么要在一个地方重复标记呢?
Inherited //代表特性是否可以继承,默认是可以继承的,一个类如果继承了标有特性的类,这个特性也会被继承。
自定义特性
特性在MVC,ORM,API等等框架中都有使用,但是有的时候我们可以自定义自己的特性,实现自已的逻辑。
建造类并继承Attribute类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _06_Attribute.AttributeArgs
{
[AttributeUsage(AttributeTargets.All,AllowMultiple = false,Inherited = true)]
class NonEmptyAttribute:Attribute
{
}
}
再类名上最好用Attribute结尾,在使用的时候则不用加Attribute。这样我们就有了一个属于自己的特性。
使用特性
using _06_Attribute.AttributeArgs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _06_Attribute.Models
{
[Obsolete("这是一个提示信息")]
class Student
{
// 自定义特性
[NonEmpty]
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
在需要的地方,满足特性的规则使用。
==注意:==到了这里其实都是没有什么用的,特性真正的使用方法是反射逻辑。
特性更像是一个flag。
反射调用特性
class CheckAttribute
{
public static void Check<T>(T t)
{
// 反射泛型,获取type
Type type = t.GetType();
CheckType(type);
}
/// <summary>
/// 检查类属性
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static bool CheckType(Type type)
{
// 查看类上是否有标记特定特性
if (type.IsDefined(typeof(NonEmptyAttribute),true))
{
//找到特性
object item = type.GetCustomAttributes(typeof(NonEmptyAttribute), true)[0];
// 将特性转换,之后就是特性使用逻辑了
Attribute attribute1 = item as Attribute;
Console.WriteLine(attribute1);
return true;
}
return false;
}
}
利用特性实现ORM中的特性验证
思想:
/// <summary>
/// 属性检测类,这个检测类就看想象使用了,比如在ORM中,可以使用特性检测是否非空,字符长度等,都可以使用特性检测方法。
/// 当然这个类是需要封装的,在什么地方调用合适??是否需要封装一个构造类?当调用一个构造方法时,使用这个类来构造,在构造完成后
/// 调用属性检测方法,如果检测合格,则返回,若不合格则销毁?但是性能会有损耗吧,都构建出来了才检测,万一失败则销毁,这样会浪费构造的。
/// </summary>
实现:
1、建立抽象父类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _06_Attribute.AttributeArgs
{
public abstract class BaseAttribute : Attribute
{
public abstract bool CheckAttribute(object t);
}
}
2、子类特性实现父类抽象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _06_Attribute.AttributeArgs
{
// 这是一个非空检测特性
class RequirdAttribute : BaseAttribute
{
public override bool CheckAttribute(object tvalue)
{
if (tvalue.ToString()=="0"||tvalue.ToString()=="null")
{
return false;
}
return true;
}
}
}
3、特性验证类
using _06_Attribute.AttributeArgs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
namespace _06_Attribute
{
class CheckAttribute
{
public static string Check<T>(T t)
{
// 反射泛型,获取type
Type type = t.GetType();
// 类检测
if (!CheckType(type))
{
return "类型特性不符!!!";
}
// 属性检测
if (!CheckProperties(type,t))
{
return "属性特性不符!!!";
}
// 检测字段
if (!CheckFields(type))
{
return "字段特性不符!!!";
}
// 检测方法
if(!CheckMethods(type))
{
return "方法特性不符!!!";
}
return "成功";
}
private static bool CheckMethods(Type type)
{
foreach (var item in type.GetMethods())
{
if (item.IsDefined(typeof(BaseAttribute), true))
{
object v = item.GetCustomAttributes(typeof(BaseAttribute), true)[0];
BaseAttribute attribute = v as BaseAttribute;
if (attribute.CheckAttribute(item))
{
return true;
}
else
{
return false;
}
}
return true;
}
return true;
}
private static bool CheckFields(Type type)
{
foreach (var item in type.GetFields())
{
if (item.IsDefined(typeof(BaseAttribute), true))
{
object v = item.GetCustomAttributes(typeof(BaseAttribute), true)[0];
BaseAttribute attribute = v as BaseAttribute;
Console.WriteLine(item.GetValue(type));
if (attribute.CheckAttribute(item.GetValue(type)))
{
return true;
}
else
{
return false;
}
}
return true;
}
return true;
}
private static bool CheckProperties(Type type,object t)
{
foreach (var item in type.GetProperties())
{
if (item.IsDefined(typeof(BaseAttribute), true))
{
object v = item.GetCustomAttributes(typeof(BaseAttribute), true)[0];
BaseAttribute attribute = v as BaseAttribute;
//Console.WriteLine(item.GetValue(t));
if (attribute.CheckAttribute(item.GetValue(t)))
{
return true;
}
else
{
return false;
}
}
return true;
}
return true;
}
/// <summary>
/// 检查类属性
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static bool CheckType(Type type)
{
// 查看类上是否有标记特定特性
if (type.IsDefined(typeof(BaseAttribute),true))
{
//找到特性
object item = type.GetCustomAttributes(typeof(BaseAttribute), true)[0];
// 将特性转换,之后就是特性使用逻辑了
BaseAttribute attribute = item as BaseAttribute;
if (attribute.CheckAttribute(type))
{
return true;
}
else
{
return false;
}
}
return true;
}
}
}
4、使用调用
Student student = new Student() {Age=15 };
string v = CheckAttribute.Check<Student>(student);
Console.WriteLine(v);
总结:
特用并不难,只是需要一种思想,特性有类似于AOP的思想,就是在不破坏原有代码结构的基础上,可以增加一些逻辑扩展,但是还是有一些局限性的。要多多使用。