这篇文章主要面向的对象是初级开发人员,以及对属性不是很熟悉的开发人员。
属性在C#中很常用,但有部分开发人员对它既熟悉又陌生。概念上属性是将元数据关联到元素的方式。属性的使用方法我们在代码中经常肩见到,比如下面这样的:
[Test]
public class MyClass
{
//more code
}
在上面的样例代码中Test就是一个属性。属性是放在类、字段和方法等定义的前面(上面),用来指定特定内容的。.Net框架中为我们提供了一些常用属性。比如Serializable,它告诉编译器当前类可以序列化成JSON或XML。
TIP:属性在编译的时候会嵌入到程序集中。我们可以使用反射来获得属性的值。
当.Net框架提供的属性不足以满足我们开发的要求时,我们可以自定义属性,自定义属性在项目中算比较常用的技术。定义自定义属性需继承抽象类System.Attribute。比如当创建一个汽车类,需要一个属性来表示汽车的品牌、型号时,我们可以像下面代码这样实现自定义属性:
public class CarAttribute : Attribute
{
public string Brand { get; set; }
public string Model { get; set; }
public CarAttribute (string brand, int model)
{
Brand = brand;
Model = model;
}
}
通过上面的代码我们可以看出,属性其实就是一个类,它和其它类一样,也拥有字段、方法、构造函数等成员。
在本文的前面说过,属性可以放在类、字段和方法等定义的前面(上面),那么,我们来看一下如何使用上一小节中自定义的属性,代码如下:
[Car("BMW", "x3")]
public class Carriage
{
//more code
}
在这里这儿需要注,自定义属性的名字,如果我使用的是xxx+Attribute的形式来命名名称的话,那么在使用时可以用短名称xxx(例如上面代码中的Car就是使用的是CarAttribute的短名称)
属性本身是一个类,因此属性也可以用其他属性来指定和修饰。常用的修饰属性的属性是AttributeUsage 属性,它用来限制自定义属性可以修饰的元素类型,例如我们将CarAttribute属性的使用范围限制为类和接口,可以这么做:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class CarAttribute: Attribute
{
//more code
}
AttributeUsage属性的参数AttributeTargets是一个枚举,包括类、接口、方法、构造函数、枚举、程序集等枚举内容。经过修改后的CarAttribute属性只能用在类和接口中,如果用它来修饰字段,编译器就会报错。 AttributeUsage还允许我们定义从修饰对象继承的对象,是否也获得该属性。同样我们将CarAttribute修改为从修饰对象继承的对象可以获得该属性:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = true)]
public class CarAttribute: Attribute
{
//more code
}
同时我们也能指定属性是否可以在一个元素上有多个实例,例如下面的代码就定义了CarAttribute属性不允许在一个元素上有多个实例:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]
public class CarAttribute: Attribute
{
//more code
}
到这里一定有小伙伴问,如何访问属性呢?其实.NET框架为我们提供了Attribute.GetCustomAttribute()方法来访问属性,简单的用法如下:
var carType = typeof(Carriage);
var attributeType = typeof(CarAttribute);
var attribute = (CarAttribute)Attribute.GetCustomAttribute(carType , attributeType);
Console.WriteLine($"Car is {attribute.Brand} {attribute.Model}");
除了上面的访问方式外,还有一种常用的访问方式:反射访问。反射的主要的作用是用来收集对象的数据而不是对象本身的数据。这些数据包括对象的类型、对象的成员的信息、特定程序集信息以及存储在元素属性中的任何信息。最简单的反射是GetType()方法,代码如下:
int myNum= 88;
Type type = myNum.GetType();
Console.WriteLine(type);
我们还可以使用反射来获取关于包含给定类型的程序集的信息:
Assembly mobileAssembly = typeof(Carriage).Assembly;
Console.WriteLine(mobileAssembly);
反射同样可以获得属性的信息和对象构造函数的信息:
PropertyInfo property = reflected.GetType().GetProperty("PropertyOne");
Console.WriteLine(property);
ConstructorInfo constructor = reflected.GetType().GetConstructor(new Type[0]);
Console.WriteLine(constructor);
属性将元数据分配给类、字段、方法等。元数据在构建项目时被编译并描述元素而不是元素的数据。我们可以继承Attribute类来自定义属性,并使用AttributeUsage属性限制属性的使用范围,并通过反射来获取属性数据。