c#特性实现模型的主键
c#特性
2019-03-06 19:26:00 weixin_30657541 阅读数 2

c#当中的特性分为系统特性和自定义特性,就现在我们学习两种系统特性。

特性一:Obsolete,检查方法是否过时。当然在其中的方法重载中,默认是不报错的,最后的参数可以自己选择。

using System.Reflection;
using System.Diagnostics;

namespace 特性
{
    class Program
    {
        [Obsolete("这是一个过时的方法,请检查",true)]
        public void OldMethod()
        {
            Console.WriteLine("这是一个旧方法");
        }

        public void NewMethod()
        {
            Console.WriteLine("这是一个新方法");
        }

        public void Test()
        {
            OldMethod();
            NewMethod();
        }

        static void Main(string[] args)
        {
            Program obj = new Program();
            obj.Test();
        }
    }
}

特性二:Conditional,实现条件编译,阻止一个方法的进行,比如测试方法。

#define 测试
#define debug

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Diagnostics;
using System.Reflection;

namespace 特性
{
    class Demo1
    {
        [Conditional("测试")]
        public void TestMethod()
        {
            Console.WriteLine("这是一个测试方法");
        }

        public void Method1()
        {
            Console.WriteLine("这是方法1");
        }

        public void Method2()
        {
            Console.WriteLine("这是方法2");
        }

        public void Test()
        {
#if debug
            TestMethod();
#else
            Method1();
#endif
            Method1();
        }

        static void Main(string[] args)
        {
            Demo1 obj = new Demo1();
            obj.Test();
        }
    }
}

当然实现条件编译还有使用“预编译”指令,

#if debug

#else

#endif

当然不管是Conditional特性,还是预编译指令,都可以选择某个方法是否可以执行,也都需要宏定义去实现。

 

有梦的时候还知道去哪,别轻易丢掉,加油吧!

转载于:https://www.cnblogs.com/Optimism/p/10485439.html

C#特性
2017-05-24 23:39:00 weixin_30352645 阅读数 2

特性是一个类,需要继承或间接继承System.Attribute。

1.常见的特性

  AttributeUsage:定义特性定义到目标元素。

  Flags:将枚举值作为位标记,而非数值。

        [Flags]
        public enum Animal
        {
            Dog = 0x0001,
            Cat = 0x0002,
            Chicken = 0x0004,
            Duck = 0x0008
        }   
    
        Animal animal = Animal.Dog| Animal.Cat;
      WriteLine(animal.ToString());//Animal使用Flags特性,输出Dog,Cat;不使用Flags特性,输出3     

  DllImport:调用非托管代码。

    public class DllImportDemo
    {
        [DllImport("User32.dll")]
        public static extern int MessageBox(int hParent, string msg, string caption, int type);
        public static int Main()
        {
            return MessageBox(0, "使用特性", ".NET Attribute", 0);
        }
    }

    //调用
    DllImportDemo.Main();

 

2.尝试自己写一个特性

    [AttributeUsage(AttributeTargets.Property,  //应用于属性
        AllowMultiple = false,                  //不允许应用多次
        Inherited = false)]                     //不继承到派生类
    public class TrimAttribute : System.Attribute
    {
        public Type Type { get; set; }
        public TrimAttribute(Type type)
        {
            Type = type;
        }
    }
    /// <summary>
    /// TrimAttribute:实现扩展方法Trim()  --必须是静态类、静态方法、第一个参数用this修饰
    /// </summary>
    public static class TrimAttributeExt
    {
        public static void Trim(this object obj)
        {
            Type tobj = obj.GetType();
            foreach (var prop in tobj.GetProperties())
            {
                //GetCustomAttributes(typeof(TrimAttribute), false),返回TrimAttribute标识的特性
                foreach (var attr in prop.GetCustomAttributes(typeof(TrimAttribute), false))
                {
                    TrimAttribute tab = (TrimAttribute)attr;
                    if (prop.GetValue(obj) != null && tab.Type == typeof(string))
                    {
                        prop.SetValue(obj, GetPropValue(obj, prop.Name).ToString().Trim(), null);
                    }
                }
            }
        }
        private static object GetPropValue(object obj, string propName)
        {
            //使用指定绑定约束并匹配指定的参数列表,调用指定成员
            return obj.GetType().InvokeMember(propName, BindingFlags.GetProperty, null, obj, new object[] { });
        }
    }
特性类代码
    public class User
    {
        public int Id { get; set; }
        [Trim(typeof(string))]
        public string UserName { get; set; }
        [Trim(typeof(string))]
        public string Password { get; set; }
    }

 using static System.Console;//静态类可以使用using static
namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //DllImportDemo.Main();

            User user = new User { Id = 1, UserName = " admin     ", Password = " 1234 " };
            user.Trim();//该行行注释掉有空格
            WriteLine("|" + user.UserName + "|");

            ReadKey();
        }
    }
}
使用代码

 

转载于:https://www.cnblogs.com/Med1tator/p/6901602.html

c#特性实现模型的主键 相关内容

c#特性
2017-08-01 11:39:00 weixin_33729196 阅读数 8


想想看如果有一个消息系统,它存在这样一个方法,用来将一则短消息发送给某人:


  1. // title: 标题;author:作者;content:内容;receiverId:接受者Id 
  2. public bool SendMsg(string title, string author, string content, int receiverId){ 
  3.     // Do Send Action 
  4. }  

我们很快就发现这样将参数一个个罗列到方法的参数列表中扩展性很糟糕,我们最好定义一个Message类将短消息封装起来,然后给方法传递一个Message对象:


  1. public class Message{ 
  2.     private string title; 
  3.     private string author; 
  4.     private string content; 
  5.     private int receiverId; 
  6.     // 略 
  7. public bool SendMsg(Messag msg){ 
  8.     // Do some Action 
  9. }  

此时,我们或许应该将旧的方法删除,用这个扩展性更好的SendMsg方法来取代。遗憾的是我们往往不能,因为这组程序可能作为一组API发布,在很多客户程序中已经在使用旧版本的SendMsg()方法,如果我们在更新程序的时候简单地删除掉旧的SendMsg()方法,那么将造成使用老版本SendMsg()方法的客户程序不能工作。

这个时候,我们该如果做呢?我们当然可以通过方法重载来完成,这样就不用删除旧的SendMsg()方法了。但是如果新的SendMsg()不仅优化了参数的传递,并且在算法和效率上也进行了全面的优化,那么我们将会迫切希望告知客户程序现在有一个全新的高性能SendMsg()方法可供使用,但此时客户程序并不知道已经存在一个新的SendMsg方法,我们又该如何做呢?我们可以打电话告诉维护客户程序的程序员,或者发电子邮件给他,但这样显然不够方便,最好有一种办法能让他一编译项目,只要存在对旧版本SendMsg()方法的调用,就会被编译器告知。

.Net 中可以使用特性来完成这一工作。特性是一个对象,它可以加载到程序集及程序集的对象中,这些对象包括 程序集本身、模块、类、接口、结构、构造函数、方法、方法参数等,加载了特性的对象称作特性的目标

特性的英文名称叫做Attribute,在有的书中,将它翻译为“属性”;另一些书中,将它翻译为“特性”;由于通常我们将含有get和/或set访问器的类成员称为“属性”(英文Property),所以本文中我将使用“特性”这个名词,以区分“属性”(Property)。

我们通过这个例子来看一下特性是如何解决上面的问题:我们可以给旧的SendMsg()方法上面加上Obsolete特性来告诉编译器这个方法已经过时,然后当编译器发现当程序中有地方在使用这个用Obsolete标记过的方法时,就会给出一个警告信息。


  1. namespace Attribute { 
  2.  
  3.     public class Message {} 
  4.     
  5.     public class TestClass { 
  6.        // 添加Obsolete特性 
  7.        [Obsolete("请使用新的SendMsg(Message msg)重载方法")] 
  8.        public static void ShowMsg() { 
  9.            Console.WriteLine("这是旧的SendMsg()方法"); 
  10.        } 
  11.  
  12.        public static void ShowMsg(Message msg) { 
  13.            Console.WriteLine("新SendMsg()方法"); 
  14.        } 
  15.  
  16.     } 
  17.  
  18.     class Program { 
  19.        static void Main(string[] args) { 
  20.            TestClass.ShowMsg(); 
  21.            TestClass.ShowMsg(new Message());          
  22.        } 
  23.     } 
  24. }  

现在运行这段代码,我们会发现编译器给出了一个警告:警告CS0618: “Attribute.TestClass.ShowMsg()”已过时:“请使用新的SendMsg(Message msg)重载方法”。通过使用特性,我们可以看到编译器给出了警告信息,告诉客户程序存在一个新的方法可供使用,这样,程序员在看到这个警告信息后,便会考虑使用新的SendMsg()方法。

通过上面的例子,我们已经大致看到特性的使用方法:首先是有一对方括号“[]”,在左方括号“[”后紧跟特性的名称,比如Obsolete,随后是一个圆括号“()”。和普通的类不同,这个圆括号不光可以写入构造函数的参数,还可以给类的属性赋值,在Obsolete的例子中,仅传递了构造函数参数。

使用构造函数参数,参数的顺序必须同构造函数声明时的顺序相同,所有在特性中也叫位置参数(Positional Parameters),与此相应,属性参数也叫做命名参数(Named Parameters)。

如果不能自己定义一个特性并使用它,我想你怎么也不能很好的理解特性,我们现在就自己构建一个特性。假设我们有这样一个很常见的需求:我们在创建或者更新一个类文件时,需要说明这个类是什么时候、由谁创建的,在以后的更新中还要说明在什么时候由谁更新的,可以记录也可以不记录更新的内容,以往你会怎么做呢?是不是像这样在类的上面给类添加注释:


  1. //更新:jayce, 2016-9-10, 修改 ToString()方法 
  2. //更新:pop, 2016-9-18 
  3. //创建:code, 2016-10-1 
  4. public class DemoClass{ 
  5.     // Class Body 
  6. }  

这样的的确确是可以记录下来,但是如果有一天我们想将这些记录保存到数据库中作以备份呢?你是不是要一个一个地去查看源文件,找出这些注释,再一条条插入数据库中呢?

通过上面特性的定义,我们知道特性可以用于给类型添加元数据(描述数据的数据,包括数据是否被修改、何时创建、创建人,这些数据可以是一个类、方法、属性),这些元数据可以用于描述类型。那么在此处,特性应该会派上用场。那么在本例中,元数据应该是:注释类型(“更新”或者“创建”),修改人,日期,备注信息(可有可无)。而特性的目标类型是DemoClass类。

按照对于附加到DemoClass类上的元数据的理解,我们先创建一个封装了元数据的类RecordAttribute:


  1. public class RecordAttribute {    
  2.        private string recordType;      // 记录类型:更新/创建    
  3.        private string author;          // 作者    
  4.        private DateTime date;          // 更新/创建 日期    
  5.        private string memo;         // 备注    
  6.       
  7.        // 构造函数,构造函数的参数在特性中也称为“位置参数”。    
  8.        public RecordAttribute(string recordType, string author, string date) {    
  9.           this.recordType = recordType;    
  10.           this.author = author;    
  11.           this.date = Convert.ToDateTime(date);    
  12.        }    
  13.       
  14.        // 对于位置参数,通常只提供get访问器    
  15.        public string RecordType {   get { return recordType; }   }    
  16.        public string Author { get { return author; } }    
  17.        public DateTime Date { get { return date; } }    
  18.       
  19.        // 构建一个属性,在特性中也叫“命名参数”    
  20.        public string Memo {    
  21.           get { return memo; }    
  22.           set { memo = value; }    
  23.        }    
  24.    }    

注意构造函数的参数 date,必须为一个常量、Type类型、或者是常量数组,所以不能直接传递DateTime类型。

这个类不光看上去,实际上也和普通的类没有任何区别,显然不能它因为名字后面跟了个Attribute就摇身一变成了特性。那么怎样才能让它称为特性并应用到一个类上面呢?进行下一步之前,我们看看.Net内置的特性Obsolete是如何定义的:


  1. namespace System {    
  2.         [Serializable]    
  3.         [AttributeUsage(6140, Inherited = false)]    
  4.         [ComVisible(true)]    
  5.         public sealed class ObsoleteAttribute : Attribute {    
  6.        
  7.            public ObsoleteAttribute();    
  8.            public ObsoleteAttribute(string message);    
  9.            public ObsoleteAttribute(string message, bool error);    
  10.        
  11.            public bool IsError { get; }    
  12.            public string Message { get; }    
  13.         }    
  14.     }    

首先,我们应该发现,它继承自Attribute类,这说明我们的 RecordAttribute 也应该继承自Attribute类。 (一个特性类与普通类的区别是:继承了Attribute类)

其次,我们发现在这个特性的定义上,又用了三个特性去描述它。这三个特性分别是:Serializable、AttributeUsage 和 ComVisible。Serializable特性我们前面已经讲述过,ComVisible简单来说是“控制程序集中个别托管类型、成员或所有类型对 COM 的可访问性”(微软给的定义)这里我们应该注意到:特性本身就是用来描述数据的元数据,而这三个特性又用来描述特性,所以它们可以认为是“元数据的元数据”(元元数据:meta-metadata)。(从这里我们可以看出,特性类本身也可以用除自身以外的其它特性来描述,所以这个特性类的特性是元元数据。)

因为我们需要使用“元元数据”去描述我们定义的特性 RecordAttribute,所以现在我们需要首先了解一下“元元数据”。这里应该记得“元元数据”也是一个特性,大多数情况下,我们只需要掌握 AttributeUsage就可以了,所以现在就研究一下它。我们首先看上面AttributeUsage是如何加载到ObsoleteAttribute特性上面的。

[AttributeUsage(6140, Inherited = false)]

然后我们看一下AttributeUsage的定义:


  1. namespace System { 
  2.     public sealed class AttributeUsageAttribute : Attribute { 
  3.        public AttributeUsageAttribute(AttributeTargets validOn); 
  4.  
  5.        public bool AllowMultiple { get; set; } 
  6.        public bool Inherited { get; set; } 
  7.        public AttributeTargets ValidOn { get; } 
  8.     } 
  9. }  

可以看到,它有一个构造函数,这个构造函数含有一个AttributeTargets类型的位置参数(Positional Parameter) validOn,还有两个命名参数(Named Parameter)。注意ValidOn属性不是一个命名参数,因为它不包含set访问器,(是位置参数)。

这里大家一定疑惑为什么会这样划分参数,这和特性的使用是相关的。假如AttributeUsageAttribute 是一个普通的类,我们一定是这样使用的:


  1. // 实例化一个 AttributeUsageAttribute 类 
  2. AttributeUsageAttribute usage=new AttributeUsageAttribute(AttributeTargets.Class); 
  3. usage.AllowMultiple = true;  // 设置AllowMutiple属性 
  4. usage.Inherited = false;// 设置Inherited属性 

但是,特性只写成一行代码,然后紧靠其所应用的类型(目标类型),那么怎么办呢?微软的软件工程师们就想到了这样的办法:不管是构造函数的参数 还是 属性,统统写到构造函数的圆括号中,对于构造函数的参数,必须按照构造函数参数的顺序和类型;对于属性,采用“属性=值”这样的格式,它们之间用逗号分隔。于是上面的代码就减缩成了这样:


  1. [AttributeUsage(AttributeTargets.Class, AllowMutiple=true, Inherited=false)] 

可以看出,AttributeTargets.Class是构造函数参数(位置参数),而AllowMutiple 和 Inherited实际上是属性(命名参数)。命名参数是可选的。将来我们的RecordAttribute的使用方式于此相同。(为什么管他们叫参数,我猜想是因为它们的使用方式看上去更像是方法的参数吧。)假设现在我们的RecordAttribute已经OK了,则它的使用应该是这样的:


  1. [RecordAttribute("创建","张子阳","2008-1-15",Memo="这个类仅供演示")]    
  2.     public class DemoClass{    
  3.         // ClassBody    
  4.     }    
  5.        
  6.     //其中recordType, author 和 date 是位置参数,Memo是命名参数。  

从AttributeUsage特性的名称上就可以看出它用于描述特性的使用方式。具体来说,首先应该是其所标记的特性可以应用于哪些类型或者对象。从上面的代码,我们看到AttributeUsage特性的构造函数接受一个 AttributeTargets 类型的参数,那么我们现在就来了解一下AttributeTargets。

AttributeTargets 是一个位标记,它定义了特性可以应用的类型和对象。


  1. public enum AttributeTargets { 
  2.  
  3.     Assembly = 1,         //可以对程序集应用属性。 
  4.     Module = 2,              //可以对模块应用属性。 
  5.     Class = 4,            //可以对类应用属性。 
  6.     Struct = 8,              //可以对结构应用属性,即值类型。 
  7.     Enum = 16,            //可以对枚举应用属性。 
  8.     Constructor = 32,     //可以对构造函数应用属性。 
  9.     Method = 64,          //可以对方法应用属性。 
  10.     Property = 128,           //可以对属性 (Property) 应用属性 (Attribute)。 
  11.     Field = 256,          //可以对字段应用属性。 
  12.     Event = 512,          //可以对事件应用属性。 
  13.     Interface = 1024,            //可以对接口应用属性。 
  14.     Parameter = 2048,            //可以对参数应用属性。 
  15.     Delegate = 4096,             //可以对委托应用属性。 
  16.     ReturnValue = 8192,             //可以对返回值应用属性。 
  17.     GenericParameter = 16384,    //可以对泛型参数应用属性。 
  18.     All = 32767,  //可以对任何应用程序元素应用属性。 
  19. }  

因为AttributeUsage是一个位标记,所以可以使用按位或“|”来进行组合。所以,当我们这样写时:

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)

意味着既可以将特性应用到类上,也可以应用到接口上。

AllowMutiple 属性用于设置该特性是不是可以重复地添加到一个类型上(默认为false),就好像这样:


  1. [RecordAttribute("更新","jayce","2016-1-20")] 
  2. [RecordAttribute("创建","pop","2016-1-15",Memo="这个类仅供演示")] 
  3. public class DemoClass{ 
  4. // ClassBody 
  5. }  

Inherited 就更复杂一些了,假如有一个类继承自我们的DemoClass,那么当我们将RecordAttribute添加到DemoClass上时,DemoClass的子类也会获得该特性。而当特性应用于一个方法,如果继承自该类的子类将这个方法覆盖,那么Inherited则用于说明是否子类方法是否继承这个特性。

现在实现RecordAttribute应该是非常容易了,对于类的主体不需要做任何的修改,我们只需要让它继承自Attribute基类,同时使用AttributeUsage特性标记一下它就可以了(假定我们希望可以对类和方法应用此特性):


  1. [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, AllowMultiple=true, Inherited=false)] 
  2. public class RecordAttribute:Attribute { 
  3.     // 略 
  4. }  

我们已经创建好了自己的自定义特性,现在是时候使用它了。


  1. [Record("更新""code""2016-1-20", Memo = "修改 ToString()方法")]    
  2.     [Record("更新""jayce""2016-1-18")]    
  3.     [Record("创建""pop""2016-1-15")]    
  4.     public class DemoClass {         
  5.         public override string ToString() {    
  6.            return "This is a demo class";    
  7.         }    
  8.     }    
  9.        
  10.     class Program {    
  11.         static void Main(string[] args) {    
  12.            DemoClass demo = new DemoClass();    
  13.            Console.WriteLine(demo.ToString());    
  14.         }    
  15.     }    

利用反射来查看 自定义特性信息 与 查看其他信息 类似,首先基于类型(本例中是DemoClass)获取一个Type对象,然后调用Type对象的GetCustomAttributes()方法,获取应用于该类型上的特性。当指定GetCustomAttributes(Type attributeType, bool inherit) 中的第一个参数attributeType时,将只返回指定类型的特性,否则将返回全部特性;第二个参数指定是否搜索该成员的继承链以查找这些属性。  


  1. class Program {     
  2.     static void Main(string[] args) {    
  3.           Type t = typeof(DemoClass);    
  4.           Console.WriteLine("下面列出应用于 {0} 的RecordAttribute属性:" , t);    
  5.       
  6.           // 获取所有的RecordAttributes特性    
  7.           object[] records = t.GetCustomAttributes(typeof(RecordAttribute), false);    
  8.       
  9.           foreach (RecordAttribute record in records) {    
  10.               Console.WriteLine("   {0}", record);    
  11.               Console.WriteLine("      类型:{0}", record.RecordType);    
  12.               Console.WriteLine("      作者:{0}", record.Author);    
  13.               Console.WriteLine("      日期:{0}", record.Date.ToShortDateString());    
  14.               if(!String.IsNullOrEmpty(record.Memo)){    
  15.                  Console.WriteLine("      备注:{0}",record.Memo);    
  16.               }    
  17.           }    
  18.        }    
  19.    } 



作者:wjkang

来源:51CTO

c#特性实现模型的主键 相关内容

C#特性
2018-08-31 19:03:00 weixin_30444105 阅读数 1

1.Obsolete:在类上面加[Obsolete],表示过期特性

转载于:https://www.cnblogs.com/jiangyan219/articles/9567483.html

c#特性实现模型的主键 相关内容

C# 特性
2016-06-07 17:54:47 zhulongxi 阅读数 3084

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));


































c#特性实现模型的主键 相关内容

C#特性

阅读数 351

c#特性

阅读数 5

C# 特性

阅读数 312

c# 特性

阅读数 10

C#特性

阅读数 31

没有更多推荐了,返回首页