• C#特性和反射


    C#特性和反射

    .NET编译器的任务之一就是为所有定义和引用的类型生成元数据描述。除了程序集中标准的元数据外,.NET平台还支持特定(attribute)把更多的元数据嵌入到程序集中。

    .NET特性扩展了抽象的System.Attribute基类,.NET中有很多预定义的特性,例如:[DllImport]、[Obsolete]和[Serializable]等等。

    看一个Obsolete使用的例子,Obsolete特性用来标记不用的类或成员,当其他程序试图访问该项的时候,将会得到一个警告或错误描述。

    复制代码
    static class StringUtil
    {
        public static string ReverseString(string str)
        {
            Console.WriteLine("call reverseString");
            return "";
        }
    
        [Obsolete("this legacy method should not be used", true)]
        public static string LegacyReverseString(string str)
        {
            Console.WriteLine("call legacyReverseString");
            return "";
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            string str = StringUtil.LegacyReverseString("Hello World!");
            Console.Read();
        }
    }
    复制代码

    使用上面的代码,我们就会的到一个错误消息,提示我们不应该在使用这个方法了

    当然,我们也可以通过代码来添加自定义特性,在开始自定义特性之前,我们需要知道以下概念。

    自定义特性

    在代码中,我们可以创建自定义的特性类型,但是这个类型一定要直接或间接从System.Attribute派生。下面我们就定义了一个TableAttribute特性:

    复制代码
    [AttributeUsage(AttributeTargets.Class, Inherited=false, AllowMultiple=false)]
    public class TableAttribute : Attribute
    {
        public TableAttribute()
        {
        }
    
        public TableAttribute(string tableName)
        {
            this.TableName = tableName;
        }
    
        public string TableName { get; set; }
    }
    复制代码

    注意:对一个特性类名使用Attribute后缀是一个惯例。然而,当我们把特性添加到一个程序实体,可以选择省略Atrribute后缀。编译器会首先在System.Attribute的派生类中查找被添加的特性类。如果没有找到,那么编译器会添加 Attribute后缀继续查找。

    例如:当我们在代码中使用的时候,特性表现为[Obsolete],但是实际上类型是ObsoleteAttribute,而不是代码中的Obsolete。当.NET Framework进行名称转换的时候,所有的.NET特性(包括自定义特性)都将加上一个Attribute标记的后缀。

    AttributeUsage

    在上面的自定义特性中,有下面一行代码,我们给自定义特性应用了一个AttributeUsage特性。

    [AttributeUsage(AttributeTargets.Class, Inherited=false, AllowMultiple=false)]

    AttributeUsage类是一个预定义特性类,通过这个特性,我们可以控制自定义特性的使用,它描述了一个定制特性如和被使用。

    复制代码
    [Serializable]
    [AttributeUsage(AttributeTargets.Class, Inherited = true)]
    [ComVisible(true)]
    public sealed class AttributeUsageAttribute : Attribute
    {
        public AttributeUsageAttribute(AttributeTargets validOn);
    
        public bool AllowMultiple { get; set; }
       
        public bool Inherited { get; set; }
        
        public AttributeTargets ValidOn { get; }
    }
    复制代码

    通过代码可以看到,AttributeUsage有三个属性:

    • ValidOn

      从代码中可以看到ValidOn的类型为System.AttributeTargets, 而AttributeTargets本身是

      一个枚举,这样就可以通过按位"或"运算组合 AttributeTargets,从而指示哪些程序元素是有效的。

      例如:

      [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]

    • AllowMultiple

      这个属性标记了特性能否被重复放置在同一个程序实体前多次。

    • Inherited

      这个属性来控制定制特性的继承规则,表示特性能否被继承。

    从上面的介绍可以看到,创建自定义特性的大概步骤:

    1. 声明特性类,由 System.Attribute 直接或间接派生
    2. 使用AttributeUsage特性来控制自定义特性
    3. 声明特性类构造函数

    特性和反射结合

    当我们使用特性的时候,只是给程序集添加了一些元数据。当结合反射使用的时候,特性就能发挥很大的作用了。

    下面看一个特性和反射结合的例子,在例子中自定义了Table和Column特性,然后把这些特性应用到了我们的User类型上面;然后结合一个自定义的ORM类型,将对象的Inster()操作转换成SQL语句。

    复制代码
    namespace AttributeTest
    {
        [AttributeUsage(AttributeTargets.Class, Inherited=false, AllowMultiple=false)]
        public class TableAttribute : Attribute
        {
            public TableAttribute()
            {
            }
    
            public TableAttribute(string tableName)
            {
                this.TableName = tableName;
            }
    
            public string TableName { get; set; }
        }
    
        [AttributeUsage(AttributeTargets.Property, Inherited=false, AllowMultiple=false)]
        public class ColumnAttribute:Attribute
        {
            public ColumnAttribute()
            {
            }
    
            public ColumnAttribute(string columnName)
            {
                this.ColumnName = columnName;
            }
    
            public ColumnAttribute(string colunmName, DbType dbType)
            {
                this.ColumnName = colunmName;
                this.DbType = dbType;
            }
    
            public string ColumnName { get; set; }
            public DbType DbType { get; set; }
        }
    
    
        public class CustomizeORM
        {
            public void Insert(object table)
            {
                Type type = table.GetType();
                Dictionary<string, string> columnValueDict = new Dictionary<string, string>();
                StringBuilder SqlStr = new StringBuilder();
                SqlStr.Append("insert into ");
                TableAttribute tableAttribute = (TableAttribute)type.GetCustomAttributes(typeof(TableAttribute), false).First();
                SqlStr.Append(tableAttribute.TableName);
                SqlStr.Append("(");
    
                foreach (var prop in type.GetProperties())
                {
                    foreach (var attr in prop.GetCustomAttributes())
                    {
                        string value = prop.GetValue(table).ToString();
                        ColumnAttribute columnAttribute = attr as ColumnAttribute;
                        if (columnAttribute != null)
                        {
                            columnValueDict.Add(columnAttribute.ColumnName, value);
                        }
                    }
                }
    
                foreach (var item in columnValueDict)
                {
                    SqlStr.Append(item.Key);
                    SqlStr.Append(",");
    
                }
                SqlStr.Remove(SqlStr.Length - 1, 1);
                SqlStr.Append(") values('");
                foreach (var item in columnValueDict)
                {
                    SqlStr.Append(item.Value);
                    SqlStr.Append("','");
                }
                SqlStr.Remove(SqlStr.Length - 2, 2);
                SqlStr.Append(")");
                Console.WriteLine(SqlStr.ToString());
            }
        }
    
        [Table("Users")]
        public class User
        {
            [Column(ColumnName="Id",DbType=DbType.Int32)]
            public int UserID{get;set;}
            [Column(ColumnName="Name",DbType=DbType.String)]
            public string UserName { get; set; }
            [Column(ColumnName = "Age", DbType = DbType.Int32)]
            public int Age { get; set; }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                CustomizeORM customizeORM = new CustomizeORM();
                User user = new User() { UserID = 1, UserName = "Wilber", Age = 28};
                customizeORM.Insert(user);
    
                Console.Read();
            }
        }
         
    }
    复制代码

    代码的执行结果为:

    从这个例子中可以看到,通过特性给程序集加入的元数据,可以在运行时被反射程序得到并使用。

    通过IL代码可以看到特性的简化,代码中我们使用Table、Column对User类型进行修饰,在IL代码中都加上了Attribute后缀;同时还可以看到,这些特性都变成了User类型的元数据。

    总结

    通过本文介绍了.NET特性,同时介绍了自定义特性需要的基本知识。

    特性本身只是给程序集添加一些元数据,当结合反射使用的时候,这些被添加的元数据才能发挥更大的作用。

     
    分类: C#相关
  • 相关阅读:
    我用自己做的图书比价搜索买了一本书
    2.17
    最近的工作
    FireBug的Bug
    2.18
    tecent面试题解答
    .net杂记
    python的round测试
    最近在网上买书的体会
    关于迅雷评论的一个改造html css
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4348792.html
Copyright © 2020-2023  润新知