C#基礎系列——Attribute特性使用


前言:上篇 C#基礎系列——反射筆記 總結了下反射得基礎用法,這章我們來看看C#的另一個基礎技術——特性。

1、什么是特性:就博主的理解,特性就是在類的類名稱、屬性、方法等上面加一個標記,使這些類、屬性、方法等具有某些統一的特征,從而達到某些特殊的需要。比如:方法的異常捕捉,你是否還在某些可能出現異常的地方(例如數據庫的操作、文件的操作等)經常使用try...catch。這個時候如果使用特性,就可以大大減少方法里面的try...catch的使用。你只需要定義一個專門捕捉異常的特性類ExceptionExAttribute,然后給這個特性類做些特殊處理,比如給它增加一個AOP攔截的功能(AOP攔截的方式很多,有興趣可以搜搜看,園子里面很多類似的文章)。那么在可能出現異常的方法名稱上面加上一個[ExceptionEx]特性標簽,這個方法就具有自動捕捉異常的能力。還是加上官方定義:

特性提供功能強大的方法,用以將元數據或聲明信息與代碼(程序集、類型、方法、屬性等)相關聯。 特性與程序實體關聯后,即可在運行時使用名為“反射”的技術查詢特性。

特性具有以下屬性:

  • 特性可向程序中添加元數據。 元數據是有關在程序中定義的類型的信息。 所有的 .NET 程序集都包含指定的一組元數據,這些元數據描述在程序集中定義的類型和類型成員。 可以添加自定義特性,以指定所需的任何附加信息。 

  • 可以將一個或多個特性應用到整個程序集、模塊或較小的程序元素(如類和屬性)。

  • 特性可以與方法和屬性相同的方式接受參數。

  • 程序可以使用反射檢查自己的元數據或其他程序內的元數據。

       (以上來自MSDN)

 

2、為什么需要特性:這個上面已經簡單介紹過,特性能大大減少統一需求的代碼量。其他不說,至少它能讓我們的代碼看上去更大氣點吧~~

 

3、特性的使用:博主這次還是打算從三個方便分別介紹下特性的常規使用方法。當然這幾種方式都是博主原來用過的,可能不是最好的舉例場景,但是也算比較典型的特性用法吧。

(1)類的屬性上面特性的用法:

之所以將這個放在最前面介紹是因為博主最近做的一個BS項目正好用到,並且使用場景也比較典型。首先介紹下使用場景:最近項目有一個需求,BS界面需要一個拖拽的功能。如下圖

 

當將左邊的3個div拖到右邊來時,每個div都有自己的特有屬性,比如2部門拖過來時,要顯示如下屬性:

1和3部門拖過來時可能對應的屬性不同。

 

設計思路:每個div對應的Model,每個Model里面有自己特有的屬性,然后屬性上面加上特性顯示屬性的名稱和默認值,以及界面應該呈現的html標簽。

實現代碼:

首先來看自定義的一個特性類:

   public class DetailAttribute : Attribute
    {
        public string AttrName { set; get; }
        public string Html { set; get; }
        public string DefaultValue { set; get; }
        public string DataSource { set; get; }
    }

 

對應的Model:

   public class Factory
    {
        [Detail(AttrName="寬度", Html="<input type='text' />", DefaultValue="50", DataSource=null)]
        public string Width { set; get; }

        [Detail(AttrName = "高度", Html = "<input type='text' />", DefaultValue = "50", DataSource = null)]
        public string Height { set; get; }

        [Detail(AttrName = "狀態", Html = "<select></select>", DefaultValue = null, DataSource = "select text,value from status")]
        public string Status { set; get; }

        [Detail(AttrName = "Tag值", Html = "<input type='text' />", DefaultValue = "", DataSource = null)]
        public string Tag { set; get; }
    }

    public class FactoryDetail
    {
        [Detail(AttrName = "寬度", Html = "<input type='text' />", DefaultValue = "50", DataSource = null)]
        public string Width { set; get; }

        [Detail(AttrName = "高度", Html = "<input type='text' />", DefaultValue = "50", DataSource = null)]
        public string Height { set; get; }

        [Detail(AttrName = "狀態", Html = "<select></select>", DefaultValue = null, DataSource = "select text,value from status")]
        public string Status { set; get; }

        [Detail(AttrName = "Tag值", Html = "<input type='text' />", DefaultValue = "", DataSource = null)]
        public string Tag { set; get; }
        [Detail(AttrName = "描述", Html = "<input type='text' />", DefaultValue = "", DataSource = null)]
        public string Desc { set; get; }
    }

然后在界面的拖放事件結束時通過js發送ajax請求來得到界面要呈現的html:

$(".jq-draggable-outcontainer").draggable({
        helper: "clone",
        scroll: true,
        drag: function (event, ui) {
            //   debugger;
        }
    });
    $("#content").droppable({
        drop: function (event, ui) {
            //  debugger;
            if (ui.draggable[0].className.indexOf("jq-draggable-outcontainer") > 0) {
                var text = ui.draggable[0].innerText.trim();
                $(this).append('<div class="window jq-draggable-incontainer" onclick="GetPropertiesByType(\'1\',this)" style="position:absolute;left:' +(event.clientX-20) + 'px;top:' + (event.clientY-20) + 'px" id="window' + iIndex + '"><strong>' + text + '</strong></div>');

                $("#content2").html("");
                cur_selector = $("#window"+iIndex);
                $.Ewin.AjaxPost("/Home/GetModelByType", { strType: "Factory" }, function (data, status) {
                    var element = $.parseJSON(data.Json);
                    var arrProp = element.element.property;
                    //0.構造html
                    var strHtml = "<div style='float:right;padding-top:0px;width:300px;height:auto;'><table cellpadding='5' border='1'>";
                    //1.拼html構造屬性
                    strHtml += "</table></div>";
                    $("#content2").append(strHtml);
                }, function () {

                }, null);
                iIndex++;

            }
        }
    });

對應的C#方法:

     public JsonResult GetModelByType(string strType)
        {
       //strType傳過來的是Factory或者FactoryDetail
var assembly = Assembly.Load("Ewin.Client.Web");//參數為程序集的名稱 var oType = assembly.GetType("Ewin.Client.Web.Controllers." + strType);
       //得到類的所有屬性
var lstProperties = oType.GetProperties(); foreach (var oProperty in lstProperties) {
         //得到每一個屬性的特性類集合 IList
<CustomAttributeData> lstAttr = oProperty.GetCustomAttributesData(); foreach (var oAttr in lstAttr) {
            //得到每一個特性類的全稱 Console.WriteLine(
"特性類的名稱" + oAttr.AttributeType.FullName); Console.WriteLine("特性類成員如下:");
            //得到特性類的所有參數
var lstAttrArgu = oAttr.NamedArguments; foreach (var oAttrAru in lstAttrArgu) {
              //取每個特性類參數的鍵值對 Console.WriteLine(oAttrAru.MemberName
+ "=" + oAttrAru.TypedValue.Value); } //Console.WriteLine(oAttr.AttributeType+"——"+oAttr.NamedArguments); } } return Json(new { }, JsonRequestBehavior.AllowGet); }

GetModelByType方法結果簡單構造下然后將屬性的鍵值對返回給js方法,然后再由js追加到界面上面。這樣通過特性和反射的結合能很快完成這個小功能的設計。

 

(2)類的方法上面特性的用法:

這個用法.Net framework里面就很多,如果MVC里面Filter過濾器的用法:

public class SuperLogStat : ActionFilterAttribute
    {
        //模塊名稱
        private EnumModuleName moduleEnum = EnumModuleName.ModuleOther;
        //功能名稱
        private string functionName = string.Empty;
        //用戶Id
        private string userId = string.Empty;
        
        public string Version
        {
            get { return ConfigurationManager.AppSettings["UploatStatVersion"]; }
        }

        public EnumModuleName ModuleEnum
        {
            get { return this.moduleEnum; }
            set { this.moduleEnum = value; }
        }

        public string FunctionName
        {
            get { return this.functionName; }
            set { this.functionName = value; }
        }

  

     //這兩個方法都是父類的virtual方法,一個再return View()之前執行,一個再之后執行
        //
        // 摘要:
        //     在執行操作方法之前由 MVC 框架調用。
        //
        // 參數:
        //   filterContext:
        //     篩選器上下文。
        public override void OnActionExecuting(ActionExecutingContext filterContext) 
        {
            try
            {
                string userName = filterContext.HttpContext.User.Identity.Name;
                this.userId = userName.Replace("china\\", "");
            }
            catch (Exception)
            {
                this.userId = string.Empty;
            }
        }
        
        //
        // 摘要:
        //     在執行操作結果之前由 MVC 框架調用。
        //
        // 參數:
        //   filterContext:
        //     篩選器上下文。
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            UserLogUtils.LogUserStatic(this.userId, this.Version,
                EnumUtils.getEnumDescByValue((int)this.moduleEnum, typeof(EnumModuleName)),
                this.functionName);
        }

    }
View Code

在Controller里面方法上面加上特性:

//調用

[SuperLogStat(ModuleEnum = EnumModuleName.ModuleHome, FunctionName = "待審核")]

        public ActionResult MyApplyToAuditing()
        {
            return View();
        }

這個ActionFilterAttribute這個特性用法里面就有異常的攔截機制,和前面說的自定義的異常攔截是相同的。

 

(3)類上面特性的用法:

類上面特性的用法其實.Net里面也很多。比如為了避免new一個對象而使用的MEF就是一個很有說服力的例子:

在定義個類實現一個接口時:

    [Export("Impc_TB_Test", typeof(Ifc_TB_Test))]
    public class Impc_TB_Test : Ifc_TB_Test
    {
        ......
    }    

定義接口沒有任何特殊:

    public interface Ifc_TB_Test
    {
        ......
    }

然后在使用時只需要加一個[Import]標簽,這個變量就會在編譯時自動new一個Impc_TB_Test變量:

[Import("Impc_TB_Test")]
 Ifc_TB_Test service { get; set; }

在使用service變量時,就可以直接把它當做一個Impc_TB_Test對象來使用。是不是很方便。

 

這幾種常見用法都是博主用過的覺得比較好的場景,當然特性的用法肯定遠不止如此,歡迎大俠們指正拍磚~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM