目录
特性
规定特性
预定义特性
1,AttributeUsage
2,Conditional
3,Obsolete
创建自定义特性
1,声明自定义特性
2,构建自定义特性
应用自定义特性
属性
访问器
抽象属性
索引器
语法
索引器的用途
重载索引器
定义:特性是运行时传递程序中的各种元素(类、方法、结构、枚举、组件)行为信息的声明性标签。可以特过特性向程序添加声明性信息。
一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
特性的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。
[attribute(positional_parameters, name_parameter = value, ...)]
element
.NET框架提供了三种预定义特性:
AttributeUsage
Conditional
Obsolete
AttributeUsage描述了如何使用一个自定义特性类,规定了特性可运用到的项目类型,语法如下:
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]
其中,
参数validon规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。
参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。
这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符,它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。规定该特性的语法如下:
[Conditional(
conditionalSymbol
)]
例如:
#define DEBUG
using System;
using System.Diagnostics;
public class Myclass
{
[Conditional("DEBUG")]
public static void Message(string msg)
{
Console.WriteLine(msg);
}
}
class Test
{
static void function1()
{
Myclass.Message("In Function 1.");
function2();
}
static void function2()
{
Myclass.Message("In Function 2.");
}
public static void Main()
{
Myclass.Message("In Main function.");
function1();
Console.ReadKey();
}
}
结果:
In Main function.
In Function 1.
In Function 2.
标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。
规定的语法特性如下:
[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]
其中:
参数message,是一个字符串,描述项目为什么过时,以及该用什么替代。
参数iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。
实例:
using System;
public class MyClass
{
[Obsolete("Don't use OldMethod, use NewMethod instead", true)]
static void OldMethod()
{
Console.WriteLine("It is the old method");
}
static void NewMethod()
{
Console.WriteLine("It is the new method");
}
public static void Main()
{
OldMethod();
}
}
结果:
Don't use OldMethod, use NewMethod instead
这是一个错误信息说明。
.NET框架允许船舰自定义特性,由于存储声明性信息,并且可以在运行时被检索,该信息根据设计标准和应用程序的需要可以与任何目标元素相关。
创建并且使用自定义特性需要四个步骤:
声明自定义特性
构建自定义特性
在目标程序元素上应用自定义特性
特过反射访问特性
最后一个步骤包含编写一个简单的程序来读取元数据以便来查找各种符号。
一个新的自定义特性应派生自 System.Attribute 类。例如:
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
我们声明了一个DeBugInfo的特性
构建一个名为DeBugInfo的特性,它存储下面的信息:
bug的编号代码
开发人员
更新日期
存储开发人员标记的字符串消息
DeBugInfo 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。所以 bug 编号、开发人员和更新日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递。下面的代码演示了 DeBugInfo 类:
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
通过把特性放置在紧接着它的目标之前,来应用该特性:
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
// 成员变量
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012",
Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
属性是类、结构和接口的命名成员。类或结构中的成员变量或者方法称为域。属性是域的扩展,且可以使用相同的语法来访问。他们使用访问器让私有域的值可以被读写或操作。
属性不会确定存储位置,它们具有可读写或计算它们值的访问器。
属性的访问器包含有助于获取或设置属性的可执行语句。访问器声明可包含一个get访问器,set访问器,或者同时包含,例如:
// 声明类型为 string 的 Code 属性
public string Code
{
get
{
return code;
}
set
{
code = value;
}
}
// 声明类型为 string 的 Name 属性
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
// 声明类型为 int 的 Age 属性
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
实例:
using System;
namespace runoob
{
class Student
{
private string code = "N.A";
private string name = "not known";
private int age = 0;
// 声明类型为 string 的 Code 属性
public string Code
{
get
{
return code;
}
set
{
code = value;
}
}
// 声明类型为 string 的 Name 属性
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
// 声明类型为 int 的 Age 属性
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
public override string ToString()
{
return "Code = " + Code +", Name = " + Name + ", Age = " + Age;
}
}
class ExampleDemo
{
public static void Main()
{
// 创建一个新的 Student 对象
Student s = new Student();
// 设置 student 的 code、name 和 age
s.Code = "001";
s.Name = "Zara";
s.Age = 9;
Console.WriteLine("Student Info: {0}", s);
// 增加年龄
s.Age += 1;
Console.WriteLine("Student Info: {0}", s);
Console.ReadKey();
}
}
}
结果:
Student Info: Code = 001, Name = Zara, Age = 9 Student Info: Code = 001, Name = Zara, Age = 10
抽象类可拥有抽象属性,这些属性应在派生类中被实现。下面的程序说明了这点:
using System;
namespace runoob
{
public abstract class Person
{
public abstract string Name
{
get;
set;
}
public abstract int Age
{
get;
set;
}
}
class Student : Person
{
private string code = "N.A";
private string name = "N.A";
private int age = 0;
// 声明类型为 string 的 Code 属性
public string Code
{
get
{
return code;
}
set
{
code = value;
}
}
// 声明类型为 string 的 Name 属性
public override string Name
{
get
{
return name;
}
set
{
name = value;
}
}
// 声明类型为 int 的 Age 属性
public override int Age
{
get
{
return age;
}
set
{
age = value;
}
}
public override string ToString()
{
return "Code = " + Code +", Name = " + Name + ", Age = " + Age;
}
}
class ExampleDemo
{
public static void Main()
{
// 创建一个新的 Student 对象
Student s = new Student();
// 设置 student 的 code、name 和 age
s.Code = "001";
s.Name = "Zara";
s.Age = 9;
Console.WriteLine("Student Info:- {0}", s);
// 增加年龄
s.Age += 1;
Console.WriteLine("Student Info:- {0}", s);
Console.ReadKey();
}
}
}
结果:
Student Info: Code = 001, Name = Zara, Age = 9 Student Info: Code = 001, Name = Zara, Age = 10
索引器允许一个对象可以像数组一样使用下标的方式来进行访问。当为类定义一个索引器的时候,该类的行为就会像是一个虚拟的数组,可以使用数组访问运算符来访问该类的成员。
一维索引器的语法:
element-type this[int index]
{
// get 访问器
get
{
// 返回 index 指定的值
}
// set 访问器
set
{
// 设置 index 指定的值
}
}
索引器的行为的声明在某种程度上类似于属性,就像属性可以使用get,set访问器来定义索引器。但是属性返回或者设置一个特定的数据成员,而索引器返回或设置对象实例的一个特定值,它把实例数据分为更小的部分,并索引每个部分,获取或设置每个部分。
定义一个属性包括提供属性的名称。索引器定义的时候不带有名称,但是带有关键字this,它指向对象实例,例子:
using System;
namespace IndexerApplication
{
class IndexedNames
{
private string[] namelist = new string[size];
static public int size = 10;
public IndexedNames()
{
for (int i = 0; i < size; i++)
namelist[i] = "N. A.";
}
public string this[int index]
{
get
{
string tmp;
if( index >= 0 && index <= size-1 )
{
tmp = namelist[index];
}
else
{
tmp = "";
}
return ( tmp );
}
set
{
if( index >= 0 && index <= size-1 )
{
namelist[index] = value;
}
}
}
static void Main(string[] args)
{
IndexedNames names = new IndexedNames();
names[0] = "Zara";
names[1] = "Riz";
names[2] = "Nuha";
names[3] = "Asif";
names[4] = "Davinder";
names[5] = "Sunil";
names[6] = "Rubic";
for ( int i = 0; i < IndexedNames.size; i++ )
{
Console.WriteLine(names[i]);
}
Console.ReadKey();
}
}
}
结果:
Zara Riz Nuha Asif Davinder Sunil Rubic N. A. N. A. N. A.
索引器可以被重载,索引器声明的时候也可以带有多个参数,并且每个参数可以是不同类型,没必要让索引器必须是整型的,C#也许索引器可以说其他类型,例如字符串类型。
实例:
using System;
namespace IndexerApplication
{
class IndexedNames
{
private string[] namelist = new string[size];
static public int size = 10;
public IndexedNames()
{
for (int i = 0; i < size; i++)
{
namelist[i] = "N. A.";
}
}
public string this[int index]
{
get
{
string tmp;
if( index >= 0 && index <= size-1 )
{
tmp = namelist[index];
}
else
{
tmp = "";
}
return ( tmp );
}
set
{
if( index >= 0 && index <= size-1 )
{
namelist[index] = value;
}
}
}
public int this[string name]
{
get
{
int index = 0;
while(index < size)
{
if (namelist[index] == name)
{
return index;
}
index++;
}
return index;
}
}
static void Main(string[] args)
{
IndexedNames names = new IndexedNames();
names[0] = "Zara";
names[1] = "Riz";
names[2] = "Nuha";
names[3] = "Asif";
names[4] = "Davinder";
names[5] = "Sunil";
names[6] = "Rubic";
// 使用带有 int 参数的第一个索引器
for (int i = 0; i < IndexedNames.size; i++)
{
Console.WriteLine(names[i]);
}
// 使用带有 string 参数的第二个索引器
Console.WriteLine(names["Nuha"]);
Console.ReadKey();
}
}
}
结果:
Zara Riz Nuha Asif Davinder Sunil Rubic N. A. N. A. N. A. 2