C# 特性

1.特性功能:

用以将元数据或声明信息与代码(程序集、类型、方法、属性等)相关联。特性与程序实体相关联后,即可在运行时用反射技术查询特性。

2.使用特性:

特性可以放置在几乎所有的声明中(但特定的特性可能限制在其上有效的声明类型)。

2.1.普通特性:

[System.Serializable]
public class SampleClass
{
    // Objects of this type can be serialized.
}
2.2.具有 DllImportAttribute 特性的方法的声明如下:

using System.Runtime.InteropServices;
[DllImport("user32.dll")]
extern static void SampleMethod();
2.3.一个声明上可放置多个特性:

        using System.Runtime.InteropServices;

        static void MethodA([In][Out] ref double x)
        {

        }
        static void MethodB([Out][In] double x)
        {

        }
        static void MethodC([Out, In]double x)
        {

        }

2.4.某些特性对于给定实体可以指定多次,如, ConditionalAttribute就是一个可以多次使用的特性:

[Conditional("DEBUG"), Conditional("TEST1")]
void TraceMethod()
{
    // ...
}

3.特性参数:

定位参数(特性类的构造函数的参数)、属性参数(特性类中的属性),也叫命名参数

定位参数是必填的且必须按顺序指定, 属性参数是可选的且可以按任意顺序指定。定位参数必须在前。

[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]
第一个参数(DLL 名称)是定位参数并且总是第一个出现,其他参数为命名参数。 在这种情况下,两个命名参数均默认为 false,因此可将其省略。 有关默认参数值的信息,请参考各个特性的文档。

4.特性目标:

特性的目标是应用该特性的实体,特性可以应用于类、特定方法或整个程序集。默认情况下,特性应用于它后面的元素。但是,您也可以显式标识要将特性应用于类,方法还是它的参数或返回值。

若要显式标识特性目标,请使用下面的语法来应用特性到目标上:

[target : attribute-list]

特性的目标列表:

C#

Visual Basic

适用对象

assembly

Assembly

整个程序集

module

Module

当前程序集模块(不同于 Visual Basic 模块)

field

不支持

在类或结构中的字段

event

不支持

event

method

不支持

方法或 get 和 set 属性访问器

param

不支持

方法参数或 set 属性访问器参数

property

不支持

属性

return

不支持

方法、属性索引器或 get 属性访问器的返回值

type

不支持

结构、类、接口、枚举或委托

举例:

将特性应用于程序集和模块:

[assembly:AssemblyTitle("Production assembly4")]

[module:CLSCompliant(true)]
将特性应用于方法、方法参数和方法返回值:

        [method:MyAttr]
        static int Method()
        {
            return 1;
        }
        或
        [MyAttr]
        static int Method()
        {
            return 1;
        }
      // applies to return value
      [return: SomeAttr]
      int Method3() { return 0; }
      [property:SomeAttr]
      public int Age{get;set;};
       或
      [SomeAttr]
      public int Age{get;set;};
无论规定 SomeAttr 应用于什么目标,都必须指定 return 目标,即使 SomeAttr 被定义为仅应用于返回值也是如此。换言之,编译器将不使用 AttributeUsage 信息解析不明确的特性目标。

5.特性的用途:

5.1.在Web服务中, 使用WebMethod特性来标记方法, 以指示该方法可以通过SOAP协议进行调用,

5.2.描述当与本机代码进行交互操作时如何封送方法参数(MarshalAsAttribute)

5.3.描述类、方法和接口的 COM 属性。

5.4.使用 DllImportAttribute 类调用非托管代码。

5.5.在标题、版本、说明或商标方面描述您的程序集。

5.6.描述要持久性序列化类的哪些成员。

5.7.描述如何映射类成员和 XML 节点以便进行 XML 序列化。

5.8.描述方法的安全要求。

5.9.指定用于强制安全性的特性。

5.11.由实时 (JIT) 编译器控制优化,以便易于调试代码。

5.12.获取有关调用方的信息的方法。


6.C#常用特性:

6.1.全局特性:

用于整个程序集或模块:如:AssemblyVersionAttribute用于向程序集中嵌入版本信息:

[assembly: AssemblyVersion("1.0.0.0")]

6.2.过时特性:

Obsolete 属性指示某个程序实体标记为建议不再使用的一个。 每次使用Obsolete标记过的实体会出现警告或错误提示。

 [System.Obsolete("use NewMethod", true)]
 public void OldMethod() { }

6.3.条件特性:Conditional

利用 Conditional 属性,程序员可以定义条件方法。Conditional 属性通过测试条件编译符号来确定适用的条件。当运行到一个条件方法调用时,是否执行该调用,要根据出现该调用时是否已定义了此符号来确定。如果定义了此符号,则执行该调用;否则省略该调用(包括对调用的参数的计算):

<strong>#define TRACE_ON</strong>
using System;
using System.Diagnostics;

public class Trace
{
    [Conditional("TRACE_ON")]
    public static void Msg(string msg)
    {
        Console.WriteLine(msg);
    }
}

public class ProgramClass
{
    static void Main()
    {
        Trace.Msg("Now in Main...");
        Console.WriteLine("Done.");
    }<pre name="code" class="csharp">#if DEBUG
    void ConditionalMethod()
    {
    }
#endif

}

 如果没#define TRANCE_ON这一指令,则在main函数中不执行Msg方法,直接输出Done.Conditional 一般用于在程序Debug版本中使用DEBUG标识符来启用跟踪并记录功能: 

[Conditional("DEBUG")]//DEBUG指令是系统指令,不用再用#define DEBUG定义
static void DebugMethod()
{
}
此时,在Debug版本中会调用DebugMethod,在Release版本中会跳过对他的调用。效果和#if DEBUG...#endif一样

如果一个方法有多个Condigional特性,则只要有一个条件符号被定义了,则该方法就会被调用。(相当于用or连接多个特性)

[Conditional("A"), Conditional("B")]
static void DoIfAorB()
{
    // ...
}//A,B中只要有一个被定义了, 就会调用。
Condigional也可用于特性本身:

[Conditional("DEBUG")]
public class DocumentationAttribute : System.Attribute
{
    string text;

    public DocumentationAttribute(string text)
    {
        this.text = text;
    }
}

class SampleClass
{
    // This attribute will only be included if DEBUG is defined.
    [Documentation("This method displays an integer.")]//只有定义了DEBUG命令时,Dowork才能应用Documentation特性。
    static void DoWork(int i)
    {
        System.Console.WriteLine(i.ToString());
    }
}

6.4.调用方信息特性:

使用调用方信息特性, 可以获取调用方的信息, 并将它传递给方法。可以获取源代码文件路径,行号和调用方的成员名称:

CallerFilePathAttribute:包含调用方源文件在编译时的完整路径。

CallerLineNumberAttribute:在调用方法的源文件中的行号。

CallerMemberNameAttribute:方法名称或调用方的属性名称。

        static void Main(string[] args)
        {
            TraceMessage("Something happened.");
            Console.ReadKey();
        }
        public static void TraceMessage(string message,
         [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
         [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
         [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
        {
            System.Diagnostics.Trace.WriteLine("message: " + message);
            System.Diagnostics.Trace.WriteLine("member name: " + memberName);
            System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);
            System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);
        }
输出:
message: Something happened.
member name: Main
source file path: d:\Myprojects\MyTestProjects\AttributeTest\Program.cs
source line number: 17

6.5.DllImportAttribute特性:using System.Runtime.InteropServices

http://blog.csdn.net/hbqhdlc/article/details/6843650

6.5.1.使用DllImport特性可以直接调用一些已经存在的功能(如windows中的一些功能, C++中已经编写好的一些方法),它的功能是提供从非托管DLL导出的函数进行调用所必须的信息。该特性只能应用于方法, 要求

最少要提供包含入口点的dll的名称。

(托管DLL:指完全由.NET 托管代码实现的DLL,完全依赖于.NET平台的CLR运行, 受.NET CLR管控, 支持内存自动回收,对.NET平台是安全的DLL,非托管DLL:指完全或部分不是用.NET代码实现,

不依赖于.NET平台即可运行, 如COM方式的DLL,不支持内存自动回收, 对.NET平台而言,也是非安全的。)

6.5.2.说明:

6.5.2.1.DllImport只能放置在方法声明上。

6.5.2.2.DllImport具有单个定位参数:指定包含被导入方法的dll名称的dllName参数。

6.5.2.3.DllImport具有五个命名参数:

a.CallingConvention参数指示入口点的调用约定,如果未指定CallingConvention,则使用默认值CallingConvention.Winapi.

b.CharSet参数指定用再入口点的字符集, 如果未指定, 则默认CharSet.Auto.

c.EntryPoint参数给出dll中入口点的名称, 如果未指定, 则默认使用方法本身的名称。

d.ExactSpelling参数指示EntryPoint是否必须与指示的入口点的拼写完全匹配,如果未指定, 则默认false.

e.PreserveSig参数指示方法的签名被保留还是被转换,当签名被转换时, 它被转换为一个具有HRESULT返回值和该返回值的一个名为retval的附加输出参数的签名,如果未指定,则true.

f.SetLastError参数指示方法是否保留Win32"上一错误",默认false.

6.5.2.4.DllImport修饰的方法必须具有extern修饰符。


6.5.3.举例:

使用DllImport特性导入Win32的MessageBox函数:

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);

MessageBox(<span style="color:Blue;">new</span> IntPtr(0), <span style="color:#A31515;">"Hello World!"</span>, <span style="color:#A31515;">"Hello Dialog"</span>, 0);
在调用MessageBox时就会弹出提示框。

 [DllImport("kernel32.dll")]
public static extern bool Beep(int frequency, int duration);//调用Beep()API来发出声音


6.5.4.DllImport路径问题:

会按照顺序自动去寻找dll:exe所在目录->System32所在目录->环境变量目录。

所以只需要你把引用的DLL 拷贝到这三个目录下 就可以不用写路径了。



7.自定义特性:

通过定义一个特性类,可以创建您自己的自定义特性。该特性类直接或间接地从 Attribute 派生,有助于方便快捷地在元数据中标识特性定义。

[System.AttributeUsage(System.AttributeTargets.Class |
                       System.AttributeTargets.Struct,
                       AllowMultiple = true)  // multiuse attribute
]
public class Author : System.Attribute
{
    <span style="color:Blue;">private</span> <span style="color:Blue;">string</span> name;    
    <span style="color:Blue;">public</span> <span style="color:Blue;">double</span> version;
    <span style="color:Blue;">public</span> Author(<span style="color:Blue;">string</span> name) 
   {        
      <span style="color:Blue;">this</span>.name = name;      
     version = 1.0;   
   }
} 

如果特性类包含一个属性,则该属性必须为读写属性。


8.使用反射访问(检索)自定义特性:

    如果没有检索自定义特性的信息和对其进行操作的方法,则定义自定义特性并将其放置在源代码中就没有意义。使用反射,可检索用自定义特性定义的信息。主要方法是 GetCustomAttributes,它返回对象数组,这些对象在运行时等效于源代码特性。

private static void PrintAuthorInfo(Type t)
        {
            Console.WriteLine("Author information for {0}",t);
            Attribute[] attr = Attribute.GetCustomAttributes(t);
            foreach (Attribute item in attr)
            {
                if (item is Author)
                {
                    Author a = (Author)item;
                    Console.WriteLine( "{0},Version{1:f}",a.GetName(),a.version);
                }
            }
        }

[System.AttributeUsage(System.AttributeTargets.Class |
                       System.AttributeTargets.Struct,
                       AllowMultiple = true)  // Multiuse attribute.
]
    public class Author : System.Attribute
    {
        string name;
        public double version;

        public Author(string name)
        {
            this.name = name;

            // Default value.
            version = 1.0;
        }

        public string GetName()
        {
            return name;
        }
    }
// Class with the Author attribute.
    [Author("P. Ackerman")]
    public class FirstClass
    {
        // ...
    }
 PrintAuthorInfo(typeof(FirstClass));


































  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值