C#⾼级教程:特性在编程中的应⽤
引⾔
C#特性,⼀个⾮常强⼤,但对于初学者来说⾮常难懂的功能。为什么在类的前加⼀个“[Serializable]”就可以实现对类的序列化,为什么⽅法的前⾯加⼀个[DllImpot]就能为⾮托管动态链接库公开静态⼊⼝。今天我们来接开它神秘的⾯纱。
什么是特性
为了能了充分的理解特性,我们先从框架中已经有的特性开始来观察。请看下⾯的代码,下⾯的代码是通过VS按F12转到定义所得到的,我们可以看到,他定义为了⼀个class,说明特性就是⼀个类。这个类⽐较特殊,可以看到他继承⾃Attribute,并且是以Attribute结尾。
public sealed class SerializableAttribute : Attribute
{
internal static Attribute GetCustomAttribute(RuntimeType type)
{
if ((type.Attributes & TypeAttributes.Serializable) != TypeAttributes.Serializable)
return (Attribute) null;
return (Attribute) new SerializableAttribute();
}
internal static bool IsDefined(RuntimeType type)
{
return type.IsSerializable;
}
}
再看⼀个特性类,这个也是从框架中截取出来的,可以看到也有和上⾯有着⼀样的性质。
public sealed class DllImportAttribute : Attribute
{
public DllImportAttribute(string dllName)
{
this._val = dllName;
}
}
上⾯是特性的定义,特性可以放在类、属性、字段、枚举等上⾯,主要的功能就是在不修改类的前提下提供额外的信息和⾏为。对于特性的⼀些语法,⼤家⾃⼰去查⼀下⽂档就可以了解了,什么AttributeUsage是⼲什么⽤的,该如何给特性传参等等。今天我们主要来探讨⼀下我们如何⾃定义⼀个特性,然后通过⾃定义的特性怎样去改变类的⾏为和信息。
⾃定义特性
我们现在来⾃⼰搞⼀个特性出来,这个特性的功能是更改枚举的显⽰值,要注意的是,我们⾃⼰声明
的特性,如果想使⽤它,就必须通过反射来调它。如果不调⽤的话,特性在那⾥是不会起作⽤的。⾸先看⼀下这个特性类的定义,代码如下:
sealed class RemarkAttribute : Attribute
{
private readonly string _name;
public RemarkAttribute(string name)
{
_name = name;
}
public string GetName()
{
return _name;
}
}
这个特性主要是通过构造函数传⼀个值进去,然后返回这个值。再看⼀下使⽤的地⽅,代码如下:
public enum UserStates
{
[Remark("正常")]
Normal = 0,
[Remark("冻结")]
Freeze,
[Remark("删除")]
Delete,
}
public enum UserLevel
{
[Remark("员⼯")]
Employee,
[Remark("管理者")]
Manager,
[Remark("董事长")]
CEO,
}
然后,还要增加⼀个扩展⽅法,来返回特性传进去的值。这就是上⾯所说的调⽤特性,因为特性是⼀
开始在编译的时候就已经⽣成好了,它存在程序的元数据⾥⾯。我们通过扩展⽅法来调⽤,代码如下:
public static class EnumExtension
{
public static string GetRemarkValue(this Enum en)
{
Type type = en.GetType();
FieldInfo field = type.GetField(en.ToString());
if (field == null)
{
return en.ToString();
}
object[] attribute = field.GetCustomAttributes(typeof(RemarkAttribute), false);
string name = en.ToString();
foreach (RemarkAttribute remarkAttribute in attribute)
{
name = remarkAttribute.GetName();
}
return name;
}
}
看⼀下我们的运⾏效果。
可以看到我们使⽤枚举的地⽅,已经显⽰成我们特性标记出来的字符了。下⾯的代码是⼀个实体类,这个类⾥⾯⽤到了上⾯的枚举,在调⽤这个类⾥⾯的ShowPeople⽅法后,枚举值就⾃动变成特性所修饰的值了。注意ShowPeople⽅法还有⼀些判断的代码,那是通过特性来确定属性的限定值的,如果不在限定值之内就会出现错误信息。
public class People
{
public People(string name, int id)
{
Name = name;
Id = id;
}
[CheckId(10000,9999999,ErrorInfo = "ID给定的值不在范围内")]
public int Id { get; set; }
[CheckString(ErrorInfo = "Name的值不能为空")]
public string Name { get; set; }
public UserStates UserStates { get; set; }
public UserLevel UserLevel { get; set; }
public void ShowPeople()
{
string msg = string.Empty;
if (!Manage.CheckValue(this, out msg))
{
Console.WriteLine(msg);
return;
}
Console.WriteLine($"Name:{Name},Id:{Id},User Status:{UserStates.GetRemarkValue()},Level:{UserLevel.GetRemarkValue()}");
}
}
刚刚上⾯的代码只是增加了枚举的显⽰信息,下⾯我们来通过特性来增加类成员的⾏为,主动判断属性值是否合理。这⾥⽤到了⼀个特性基类和两个⼦类,然后通过Manage来调⽤特性使它⽣效,具体代码如下:
sealed class CheckIdAttribute : CheckAttribute
{
public CheckIdAttribute(long min, long max)
{
Min = min;
Max = max;
}
public long Min { get; set; }
public long Max { get; set; }
public override bool Check(object obj)
{
long value;
if (!long.TryParse(obj.ToString(), out value))
return false;
return value > Min && value < Max;
}
}
[AttributeUsage(AttributeTargets.Property)]
sealed class CheckStringAttribute : CheckAttribute
{
public CheckStringAttribute()
{
}
public override bool Check(object obj)
{
if (string.IsNullOrEmpty(obj.ToString().Trim()))
{
return false;writeline教程
}
return true;
}
}
public abstract class CheckAttribute : Attribute
{
public string ErrorInfo { get; set; }
public abstract bool Check(object obj);
}
还有⼀个显⽰类信息的特性,这⾥也展⽰出来。
sealed class InfoAttribute:Attribute
{
private readonly string _infomation;
public InfoAttribute(string infomation)
{
_infomation = infomation;
}
public string GetInfo()
{
return _infomation;
}
}
下⾯是管理类
public class Manage
{
public static string GetInfo(People people)
{
Type type = people.GetType();
string info = string.Empty;
object[] attribute = type.GetCustomAttributes(typeof(InfoAttribute), false);            foreach (InfoAttribute item in attribute)
{
info = item.GetInfo();
}
return info;
}
public static bool CheckValue(People p, out string msg)
{
Type type = p.GetType();
PropertyInfo[] propertyInfos = type.GetProperties();
foreach (PropertyInfo property in propertyInfos)
{
object[] objects = property.GetCustomAttributes(true);
foreach (object o in objects)
{
var checkAttribute = o as CheckAttribute;
if (checkAttribute != null)
{
if (!checkAttribute.Check(property.GetValue(p)))
{
msg = checkAttribute.ErrorInfo;
return false;
}
}
}
}
msg = string.Empty;
return true;
}
}
管理类中有两个⽅法,分别调⽤显⽰特性和检查特性。然后是调⽤代码
class Program
{
static void Main(string[] args)
{
People p = new People(" ", 1000) {UserStates = UserStates.Normal,UserLevel = UserLevel.CEO};
People p2 = new People("fei", 200000) {UserStates = UserStates.Freeze,UserLevel = UserLevel.Employee};
People p3 = new People("jian", 3000) {UserStates = UserStates.Delete,UserLevel = UserLevel.Manager};
p.ShowPeople();
p2.ShowPeople();
p3.ShowPeople();
Console.WriteLine(Manage.GetInfo(p));
}
}
运⾏结果
结束语
今天我们通过上⾯的代码实现了添加⾃定义的特性,并将特性增加到⼀个具体类上,可以发现通过特性可以使我们在不改变类本⾝的情况下去增信息和⾏为。⽽且通过上⾯的应⽤场景可以发现特性在平时编程过程中还是很有⽤的,希望⼤家都能够掌握它。今天的分享就到这结束了,如果有什么不对的地⽅请指出。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。