首先要说的是,可能一些刚接触C#的朋友常常容易把属性(Property)跟特性(Attribute)弄混淆,其实这是两种不同的东西。属性就是面向对象思想里所说的封装在类里面的数据字段,其形式为:
1: public class HumanBase
2: {
3: public string Name { get; set; }
4: public int Age { get; set; }
5: public int Gender { get; set; }
6: }
在HumanBase这个类里出现的字段都叫属性(Property),而特性(Attribute)又是怎样的呢?
1: [Serializable]
2: public class HumanBase
3: {
4: public string Name { get; set; }
5: public int Age { get; set; }
6: public int Gender { get; set; }
7: }
简单地讲,我们在HumanBase类声明的上一行加了一个[Serializable],这就是特性(Attribute),它表示HumanBase是可以被序列化的,这对于网络传输是很重要的,不过你不用担心如何去理解它,如何理解就是我们下面要探讨的。
C#的特性可以应用于各种类型和成员。前面的例子将特性用在类上就可以被称之为“类特性”,同理,如果是加在方法声明前面的就叫方法特性。无论它们被用在哪里,无论它们之间有什么区别,特性的最主要目的就是自描述。并且因为特性是可以由自己定制的,而不仅仅局限于.NET提供的那几个现成的,因此给C#程序开发带来了相当大的灵活性和便利。
我们还是借用生活中的例子来介绍C#的特性机制吧。
假设有一天你去坐飞机,你就必须提前去机场登机处换登机牌。登机牌就是一张纸,上面写着哪趟航班、由哪里飞往哪里以及你的名字、座位号等等信息,其实,这就是特性。它不需要你生理上包含这些属性(人类出现那会儿还没飞机呢),就像上面的HumanBase类没有IsSerializable这样的属性,特性只需要在类或方法需要的时候加上去就行了,就像你不总是在天上飞一样。
当我们想知道HumanBase是不是可序列化的,可以通过:
1: static void Main(string[] args)
2: {
3: Console.WriteLine(typeof(HumanBase).IsSerializable);
4:
5: Console.ReadLine();
6: }
拿到了登机牌,就意味着你可以合法地登机起飞了。但此时你还不知道你要坐的飞机停在哪里,不用担心,地勤人员会开车送你过去,但是他怎么知道你是哪趟航班的呢?显然还是通过你手中的登机牌。所以,特性最大的特点就是自描述。
既然是起到描述的作用,那目的就是在于限定。就好比地勤不会把你随便拉到一架飞机跟前就扔上去了事,因为标签上的说明信息就是起到限定的作用,限定了目的地、乘客和航班,任何差错都被视为异常。如果前面的HumanBase不加上Serializable特性就不能在网络上传输。
我们在顺带来介绍一下方法特性,先给HumanProperty加上一个Run方法:
1: [Serializable]
2: public class HumanPropertyBase
3: {
4: public string Name { get; set; }
5: public int Age { get; set; }
6: public int Gender { get; set; }
7:
8: public void Run(int speed)
9: {
10: // Running is good for health.
11: }
12: }
只要是个四肢健全、身体健康的人就可以跑步,那这么说,跑步就是有前提条件的,至少是四肢健全,身体健康。由此可见,残疾人和老年人如果跑步就会出问题。假设一个HumanBase的对象代表的是一位耄耋老人,如果让他当刘翔的陪练,那就直接光荣了。如何避免这样的情况呢,我们可以在Run方法中加一段逻辑代码,先判断Age大小,如果小于2或大于60直接抛异常,但是2-60岁之间也得用Switch来分年龄阶段地判断speed参数是否合适,那么逻辑就相当臃肿。简而言之,如何用特性表示一个方法不能被使用呢?OK, here we go:
1: [Obsolete("I'm so old, don't kill me!", true)]
2: public virtual void Run(int speed)
3: {
4: // Running is good for health.
5: }
上面大致介绍了一下特性的使用与作用,接下来我们要向大家展示的是如何通过自定义特性来提高程序的灵活性,如果特性机制仅仅能使用.NET提供的那几种特性,不就太不过瘾了么。
首先,特性也是类。不同于其它类的是,特性都必须继承自System.Attribute类,否则编译器如何知道谁是特性谁是普通类呢。当编译器检测到一个类是特性的时候,它会识别出其中的信息并存放在元数据当中,仅此而已,编译器并不关心特性说了些什么,特性也不会对编译器起到任何作用,正如航空公司并不关心每个箱子要去哪里,只有箱子的主人和搬运工才会去关心这些细节。假设我们现在就是航空公司的管理人员,需要设计出前面提到的登机牌,那么很简单,我们先看看最主要的信息有哪些:
1: public class BoardingCheckAttribute : Attribute
2: {
3: public string ID { get; private set; }
4: public string Name { get; private set; }
5: public int FlightNumber { get; private set; }
6: public int PostionNumber { get; private set; }
7: public string Departure { get; private set; }
8: public string Destination { get; private set; }
9: }
我们简单列举这些属性作为航空公司登机牌上的信息,用法和前面的一样,贴到HumanBase上就行了,说明此人具备登机资格。这里要简单提一下,你可能已经注意到了,在使用BoardingCheckAttribute的时候已经把Attribute省略掉了,不用担心,这样做是对的,因为编译器默认会自己加上然后查找这个属性类的。哦,等一下,我突然想起来他该登哪架飞机呢?显然,在这种需求下,我们的特性还没有起到应有的作用,我们还的做点儿工作,否则乘客面对一张空白的机票一定会很迷茫。
于是,我们必须给这个特性加上构造函数,因为它不仅仅表示登机的资格,还必须包含一些必要的信息才行:
1: public BoardingCheckAttribute(string id, string name, string flightNumber, string positionNumber, string departure, string destination)
2: {
3: this.ID = id;
4: this.Name = name;
5: this.FlightNumber = flightNumber;
6: this.PositionNumber = positionNumber;
7: this.Departure = departure;
8: this.Destination = destination;
9: }
OK,我们的乘客就可以拿到一张正式的登机牌登机了,have a good flight!
1: static void Main(string[] args)
2: {
3: BoardingCheckAttribute boardingCheck = null;
4: object[] customAttributes = typeof(HumanPropertyBase).GetCustomAttributes(true);
5:
6: foreach (var attribute in customAttributes)
7: {
8: if (attribute is BoardingCheckAttribute)
9: {
10: boardingCheck = attribute as BoardingCheckAttribute;
11:
12: Console.WriteLine(boardingCheck.Name
13: + "'s ID is "
14: + boardingCheck.ID
15: + ", he/she wants to "
16: + boardingCheck.Destination
17: + " from "
18: + boardingCheck.Departure
19: + " by the plane "
20: + boardingCheck.FlightNumber
21: + ", his/her position is "
22: + boardingCheck.PositionNumber
23: + ".");
24: }
25: }
26:
27: Console.ReadLine();
28: }
本文转自https://www.cnblogs.com/ppchouyou/archive/2008/03/31/1131593.html
约定:
1.”attribute” 和 ”attributes” 均不翻译
2.”property” 译为“属性”
3.msdn 中的原句不翻译
4.”program entity” 译为 ” 语言元素 ”
Attributes in C#
介绍
Attributes 是一种新的描述信息,我们既可以使用 attributes 来定义设计期信息(例如 帮助文件,文档的 URL ),还可以用 attributes 定义运行时信息(例如,使 XML 中的元素与类的成员字段关联起来)。我们也可以用 attributes 来创建一个“自描述”的组件。在这篇指南中我们将明白怎么创建属性并将其绑定至各种语言元素上,另外我们怎样在运行时环境下获取到 attributes 的一些信息。
定义
MSDN 中做如下定义 (ms-help://MS.MSDNQTR.2002APR.1033/csspec/html/vclrfcsh ARP spec_17_2.htm)
"An attribute is a piece of additional declarative information that is specified for a declaration."
使用预定义 Attributes
在 c# 中已有一小组预定义的 attributes ,在我们学习怎样创建自定义 attributes 前,先来了解下在我们的代码中使用那些预定义的 attributes.
1 using System;
2
3 public class AnyClass
4
5 {
6 [Obsolete( " Don't use Old method, use New method " , true )]
7
8 static void Old( ) { }
9
10 static void New( ) { }
11
12 public static void Main( )
13 {
14 Old( );
15 }
16 }
仔细看下该实例,在该实例中我们用到了 ”Obsolete”attribute ,它标记了一个不该再被使用的语言元素 ( 译者注:这里的元素为方法 ) ,该属性的第一个参数是 string 类型,它解释为什么该元素被荒弃,以及我们该使用什么元素来代替它。实际中,我们可以书写任何其它文本来代替这段文本。第二个参数是告诉编译器把依然使用这被标识的元素视为一种错误,这就意味着编译器会因此而产生一个警告。
当我们试图编译上面的上面的程序,我们会得到如下错误:
AnyClass.Old()' is obsolete: 'Don't use Old method, use New method'
开发自定义 Attributes
现在我们即将了解怎么开发自定义的 attributes 。这儿有个小小处方,有它我们就可以学会创建自定义的 attributes 。
在 C# 中,我们的 attribute 类都派生于 System.Attribute 类 ( A class that derives from the abstract class System.Attribute, whether directly or indirectly, is an attribute class. The declaration of an attribute class defines a new kind of attribute that can be placed on a declaration ) ,我们就这么行动吧。
using System;
public class HelpAttribute : Attribute
{
}
不管你是否相信我,就这样我们就已经创建了一个自定义 attribute 。现在就可以用它来装饰我们的类了,就像我们使用 obsolete attribute 一样。
[Help()]
public class AnyClass
{
}
注意:按惯例我们是用 ”Attribute“ 作为 attribute 类名的后缀,然而,当我们当我们把 attribute 绑定到某语言元素时,是不包含 “Attribute“ 后缀的。编译器首先在 System.Attribute 的继承类中查找该 attribute ,如果没有找到,编译器会把 “Attribute“ 追加到该 attribute 的名字后面,然后查找它。
但是迄今为止,该 attribute 没有任何用处。为了使它有点用处,让我们在它里面加点东西吧。
using System;
public class HelpAttribute : Attribute
{
public HelpAttribute(String Descrition_in)
{
this .description = Description_in;
}
protected String description;
public String Description
{
get
{
return this .description;
}
}
}
[Help( " this is a do-nothing class " )]
public class AnyClass
{
}
在上面的例子中,我们在 attribute 类中添加了一个属性,在最后一节中,我们将在运行时查询该属性。
定义或控制自定义 Attribute 的用法
AttributeUsage 类是另一预定义类 ( 译者注: attribute 类本身用这个 atrribute System.AttributeUsage 来标记 ) ,它将帮助我们控制我们自定义 attribute 的用法,这就是,我们能为自定义的 attribute 类定义 attributes 。
它描述了一个自定义 attribute 类能被怎样使用。
AttributeUsage 提供三个属性,我们能将它们放置到我们的自定义 attribute 类上, 第一个特性是:
ValidOn
通过这个属性,我们能指定我们的自定义 attribute 可以放置在哪些语言元素之上。这组我们能把自定义 attribute 类放置其上的语言元素被放在枚举器 AttributeTargets 中。我们可以使用 bitwise( 译者注:这个词不知道怎么翻译好,但他的意思是可以这么用 : [AttributeUsage ( ( AttributeTargets)4 , AllowMultiple = false , Inherited = false )], 4 代表就是 “ class ” 元素,其它诸如 1 代表“ assembly ”, 16383 代表“ all ”等等 ) 或者 ”.” 操做符绑定几个 AttributeTargets 值。 (译者注:默认值为 AttributeTargets.All )
AllowMultiple
该属性标识我们的自定义 attribte 能在同一语言元素上使用多次。 ( 译者注:该属性为 bool 类型,默认值为 false ,意思就是该自定义 attribute 在同一语言元素上只能使用一次 )
Inherited
我们可以使用该属性来控制我们的自定义 attribute 类的继承规则。该属性标识我们的自定义 attribute 是否可以由派生类继承。( (译者注:该属性为 bool 类型,默认值为 false ,意思是不能继承)
让我们来做点实际的东西吧,我们将把 AttributeUsage attribute 放置在我们的 help attribute 上并在它的帮助下,我们来控制 help attribute 的用法。
using System;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false , Inherited = false )]
public class HelpAttribute : Attribute
{
public HelpAttribute(String Description_in)
{
this .description = Description_in;
}
protected String description;
public String Description
{
get
{
return this .description;
}
}
}
首先我们注意 AttributeTargets.Class . 它规定这个 help attribute 只能放置在语言元素 ”class” 之上。这就意味着,下面的代码将会产生一个错误。
AnyClass.cs: Attribute 'Help' is not valid on this declaration type. It is valid on 'class' declarations only.
现在试着把它绑定到方法。
[Help( " this is a do-nothing class " )]
public class AnyClass
{
[Help( " this is a do-nothing method " )] // error
public void AnyMethod()
{
}
}
我们可以使用 AttributeTargets.All 来允许 Help attribute 可以放置在任何预定义的语言元素上,那些可能的语言元素如下 :
第一种情况 如果我们查询(我们将在后面来了解如何在运行时来查询 attributes )派生类中的 help attribute ,我们将不可能查询到因为 ”Inherited” 被设为了 false 。 第二种情况 第二种情况没有什么不同,因为其 ”Inherited” 也被设为了 false 。 第三种情况 为了解释第三种和第四种情况,让我们为派生类也绑定同一 attribute 。
[Help( " BaseClass " )]
public class Base
{
}
public class Derive : Base
{
}
[Help( " BaseClass " )]
public class Base
{
}
[Help( " DeriveClass " )]
public class Derive : Base
{
}
Attributes 标记 假设,我们想把 Help attribute 绑定至元素 assembly 。第一个问题是我们要把 Help attribute 放在哪儿才能让编译器确定该 attribute 是绑定至整个 assembly 呢?考虑另一种情况,我们想把 attribute 绑定至一个方法的返回类型上,怎样才能让编译器确定我们是把 attribute 绑定至方法的返回类型上,而不是整个方法呢? 为了解决诸如此类的含糊问题,我们使用 attribute 标识符,有了它的帮助,我们就可以确切地申明我们把 attribute 绑定至哪一个语言元素。 例如 : [ assembly: Help( "this a do-nothing assembly" )] 这个在 Help attribute 前的 assembly 标识符确切地告诉编译器,该 attribute 被绑定至整个 assembly 。可能的标识符有:
在运行时查询 Attributes 现在我们明白怎么创建 attribtes 和把它们绑定至语言元素。是时候来学习类的使用者该如何在运行时查询这信息。 为了查询一语言元素上绑定的 attributes ,我们必须使用反射。反射有能力在运行时发现类型信息。 我们可以使用 .NET Framework Reflection APIs 通过对整个 assembly 元数据的迭代,列举出 assembly 中所有已定义的类,类型,还有方法。 记住那旧的 Help attribute 和 AnyClass 类。
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false ,
Inherited = false )]
public class HelpAttribute : Attribute
{
public HelpAttribute(String Description_in)
{
this .description = Description_in;
this .verion = " No Version is defined for this class " ;
}
protected String description;
public String Description
{
get
{
return this .description;
}
}
protected String version;
public String Version
{
get
{
return this .version;
}
// if we EVE r want our attribute user to set this property,
// we must specify set method for it
set
{
this .verion = value;
}
}
}
[Help( " This is Class1 " )]
public class Class1
{
}
[Help( " This is Class2 " , Version = " 1.0 " )]
public class Class2
{
}
[Help( " This is Class3 " , Version = " 2.0 " ,
Description = " This is do-nothing class " )]
public class Class3
{
}
using System;
using System.Reflection;
using System.Diagnostics;
// attaching Help attribute to entire assembly
[assembly : Help( " This Assembly demonstrates custom attributes
creation and their run - time query. " )]
// our custom attribute class
public class HelpAttribute : Attribute
{
public HelpAttribute(String Description_in)
{
//
// TODO: Add constructor logic here
this .description = Description_in;
//
}
protected String description;
public String Description
{
get
{
return this .deescription;
}
}
}
// attaching Help attribute to our AnyClass
[HelpString( " This is a do-nothing Class. " )]
public class AnyClass
{
// attaching Help attribute to our AnyMethod
[Help( " This is a do-nothing Method. " )]
public void AnyMethod()
{
}
// attaching Help attribute to our AnyInt Field
[Help( " This is any Integer. " )]
public int AnyInt;
}
class QueryApp
{
public static void Main()
{
}
}
class QueryApp
{
public static void Main()
{
HelpAttribute HelpAttr;
// Querying Assembly Attributes
String assemblyName;
Process p = Process.GetCurrentProcess();
assemblyName = p.ProcessName + " .exe " ;
Assembly a = Assembly.LoadFrom(assemblyName);
foreach (Attribute attr in a.GetCustomAttributes( true ))
{
HelpAttr = attr as HelpAttribute;
if ( null != HelpAttr)
{
Console.WriteLine( " Description of {0}:\n{1} " ,
assemblyName,HelpAttr.Description);
}
}
}
}
class QueryApp
{
public static void Main()
{
Type type = typeof (AnyClass);
HelpAttribute HelpAttr;
// Querying Class Attributes
foreach (Attribute attr in type.GetCustomAttributes( true ))
{
HelpAttr = attr as HelpAttribute;
if ( null != HelpAttr)
{
Console.WriteLine( " Description of AnyClass:\n{0} " ,
HelpAttr.Description);
}
}
// Querying Class-Method Attributes
foreach (MethodInfo method in type.GetMethods())
{
foreach (Attribute attr in method.GetCustomAttributes( true ))
{
HelpAttr = attr as HelpAttribute;
if ( null != HelpAttr)
{
Console.WriteLine( " Description of {0}:\n{1} " ,
method.Name,
HelpAttr.Description);
}
}
}
// Querying Class-Field (only public) Attributes
foreach (FieldInfo field in type.GetFields())
{
foreach (Attribute attr in field.GetCustomAttributes( true ))
{
HelpAttr = attr as HelpAttribute;
if ( null != HelpAttr)
{
Console.WriteLine( " Description of {0}:\n{1} " ,
field.Name,HelpAttr.Description);
}
}
}
}
}
[Help( " this is a do-nothing class " )]
[Help( " it contains a do-nothing method " )]
public class AnyClass
{
[Help( " this is a do-nothing method " )] // error
public void AnyMethod()
{
}
}
本文转自https://www.cnblogs.com/yangxx-1990/p/10919612.html
System.Runtime.InteropServices提供了相应的类或者方法来支持托管/非托管模块间的互相调用。 System.Runtime.InteropServices中几个比较重要的类: DllImportAttribute : 该类提供对非托管动态链接库进行引用的方法,并告诉我们的编译器该程序的静态入口点是非托管的动态连接库,它的静态属性提供了对非托管动态链接库进行调用所必需的信息,作为最基本的要求,该类应该定义提供调用的非托管动态链接库的名称。成员详细信息 StructLayoutAttribute: 该类使得用户可以控制类或结构的数据字段的物理布局。
[StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]
public class MySystemTime
{
[FieldOffset(0)]public ushort wYear;
[FieldOffset(2)]public ushort wMonth;
[FieldOffset(4)]public ushort wDayOfWeek;
[FieldOffset(6)]public ushort wDay;
[FieldOffset(8)]public ushort wHour;
[FieldOffset(10)]public ushort wMinute;
[FieldOffset(12)]public ushort wSecond;
[FieldOffset(14)]public ushor wMilliseconds;
}
MarshalAsAttribute : 指示如何在托管代码和非托管代码之间封送数据。下面是MSDN给出的示例代码:
[C#]
//Applied to a parameter.
public void M1 ([MarshalAs(UnmanagedType.LPWStr)]String msg);
//Applied to a field within a class.
class MsgText {
[MarshalAs(UnmanagedType.LPWStr)] Public String msg;
}
//Applied to a return value.
[return: MarshalAs(UnmanagedType.LPWStr)]
public String GetMessage();
一个将三个类综合运用的实例:调用kernel32.dll中的非托管方法"GetSystemTime"将系统时间返回给定制的类MySystemTime并执行输出.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace DllImportTest
{
/// <summary>
/// 定义一个用于接收非托管API方法返回值的类
/// StructLayout定义了该类的各个成员在物理上的排列方式
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 16, CharSet = CharSet.Ansi)]
public class MySystemTime
{
[FieldOffset(0)]
public ushort wYear;
[FieldOffset(2)]
public ushort wMonth;
[FieldOffset(4)]
public ushort wDayOfWeek;
[FieldOffset(6)]
public ushort wDay;
[FieldOffset(8)]
public ushort wHour;
[FieldOffset(10)]
public ushort wMinute;
[FieldOffset(12)]
public ushort wSecond;
[FieldOffset(14)]
public ushort wMilliseconds;
}
/// <summary>
/// 用LibWrapper的静态方法来调用非托管API方法"GetSystemTime"
/// </summary>
class LibWrapper
{
[DllImport("kernel32.dll", EntryPoint = "GetSystemTime")]
//如果定义的方法名称与要进行封装的非托管API方法不同则需要在DLLImport中指定入口点.
public static extern void gettime([MarshalAs(UnmanagedType.LPStruct)]MySystemTime st);
}
class TestApplication
{
public static void Main()
{
try
{
MySystemTime sysTime = new MySystemTime();
//LibWrapper.GetSystemTime(sysTime);
LibWrapper.gettime(sysTime);
Console.WriteLine("The System time is {0}/{1}/{2} {3}:{4}:{5}", sysTime.wDay,
sysTime.wMonth, sysTime.wYear, sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
}
catch (TypeLoadException e)
{
Console.WriteLine("TypeLoadException : " + e.Message);
}
catch (Exception e)
{
Console.WriteLine("Exception : " + e.Message);
}
}
}
}