基礎拾遺
前言
本來7月份想着是用一個月把基礎拾遺寫完的,結果斷斷續續寫了4個月了,才寫了這幾篇,這兩天又規划着多寫幾篇,希望能堅持吧。前兩天一次和同事聊天提到了特性Attribute,竟然有的同事都不知道它的存在。所以就在周末的時候總結了一下。
1.特性概念
特性(Attribute)是用於在運行時傳遞程序中各種元素(比如類、方法、結構、枚舉、組件等)的行為信息的聲明性標簽。您可以通過使用特性向程序添加聲明性信息。一個聲明性標簽是通過放置在它所應用的元素前面的方括號([ ])來描述的。
特性(Attribute)用於添加元數據,如編譯器指令和注釋、描述、方法、類等其他信息。比如我們在執行方法之前,要先判斷一下用戶有沒有執行這個方法的權限,方法出現異常的時候怎么去處理。
1.1.枚舉
為啥要在這聊枚舉,是不是sa,我把代碼貼出來。
enum Fruit { [Description("蘋果")] Apple, [Description("橙子")] Orange, [Description("西瓜")] Watermelon }
Ok,你只需要F12 Description 會看到
public DescriptionAttribute(string description);
這個家伙,沒錯DescriptionAttribute(string description),它就是一個特性。現在知道了吧你其實也在用到特性,只是你知不到而已。這個開篇和以往不一樣,並且廢話有點多。尷尬了,現在回來。
2.預定義特性
注:1.特性大致分為兩種特性預定義特性和定制特性。
2.預定義特性.Net 框架提供了三種預定義特性:
2.1.AttributeUsage(AttributeUsageAttribute 類):
2.1.1.作用:
描述了如何使用一個自定義特性類。它規定了特性可應用到的項目的類型。
2.1.2.使用:
[AttributeUsage(validon, AllowMultiple = false, Inherited = true)]
//限定特性類的應用范圍 (這里規定ClassMsg這個特性類只能用於類和字段) [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, AllowMultiple = true, Inherited = false)] //定制MsgAttribute特性類,繼承於Attribute public class ClassMsgAttribute : Attribute { //定義_msg字段和Msg屬性//Msg屬性用於讀寫msg字段 string _msg; public string Msg { get { return _msg; } set { _msg = value; } } public ClassMsgAttribute() { } //重載構造函數接收一個參數,賦值給_msg字段 public ClassMsgAttribute(string s) { _msg = s; } }
//調用 //在Person類上標記ClassMsg特性 [ClassMsg(Msg = "這是關於人的姓名信息的類")] class Person { //在_name字段上應用ClassMsg特性 [ClassMsg("這是存儲姓名的字段")] string _name; //以下特性無法應用,因為MsgAttribute定義的特性只能用於類和字段 //[ClassMsg("這是讀寫姓名字段的屬性")] public string Name { get { return _name; } set { _name = value; } } } }
以上是AttributeUsage使用時的一個小栗子。
2.1.3.AttributeUsage 參數說明
validon:被放置的語言元素,它是枚舉器 AttributeTargets 的值的組合。默認值是 AttributeTargets.All。
AllowMultiple:該值指示能否為一個程序元素指定多個指示屬性實例。如果允許指定多個實例,則為 true;否則為 false。默認為 false。
Inherited:該值指示指示的屬性能否由派生類和重寫成員繼承。如果該屬性可由派生類和重寫成員繼承,則為 true,否則為 false。默認為 true。
2.2.Obsolete(ObsoleteAttribute 類):
2.2.1.作用:
這個單詞是廢棄的意思,其實這個特性也是用來 就是舍棄這個方法,生成一個錯誤或警告。
2.2.2.用法:
[Obsolete( message, iserror)]
[Obsolete("這個方法不在用來", true)] static void OldMethod() { Console.WriteLine("不執行的"); }
2.2.3.Obsolete參數說明
message:告訴你為什么廢棄它
iserror,是一個布爾值。如果該值為 true,編譯器應把該項目的使用當作一個錯誤。默認值是 false(編譯器生成一個警告)
2.3.Conditional(ConditionalAttribute 類)
2.3.1.作用:
據預處理標識符執行方法;至於預處理器指令,就需要大家自己去了解了。
2.3.2.用法
[Conditional( conditionalSymbol)]
由於我不了解c#預處理器指令,所以這個真的不會用,所以就不往下寫了等我了解了預處理器指令再補上吧。
3.定制特性
3.1.定制特性
定制特性:利用定制特性可以宣告自己的代碼構造添加注釋來實現特殊功能。它允許允許幾乎每個元數據表記錄項定義和應用信息。這種可擴展的元數據信息能在運行時查詢,從而動態改變代碼的執行方式。使用各種net framework技術會發現他們都利用了地址特性。目的是為了方便開發者在代碼中表達他們的意圖。
3.2.使用定制特性
.net framework類庫(fcl)中定義了幾百個類庫,可以將它們用到自己代碼中的各種元素。
舉幾個小栗子:
3.2.1.Description:開篇我們就說枚舉這個家伙,是的Description它就是框架中用來定義枚舉的一個定制特性。它的定義如下:
[AttributeUsage(AttributeTargets.Field,AllowMultiple =true,Inherited = true)] class DescriptionAttribute:Attribute { private string description; public string Description { get { return description; } } public DescriptionAttribute(String description) { this.description = description; } }
AttributeTargets.Field:表示此特性只用於字段;DescriptionAttribute(String description) 他的構造函數有一個參數用來進行性字段描述,我們在操作枚舉的時候可以讓這個描述現在在我們的頁面上,想想那些還在用字典關聯的做法是不是這種更方便呢?
enum Fruit { [Description("蘋果")] Apple, [Description("橙子")] Orange, [Description("西瓜")] Watermelon }
Flags:用於枚舉類型,枚舉類型就成為了位標志(big flag)集合。
Seriaklizable:用與類型,告訴序列化器一個是空的字段可以序列化和反序列化。
dllimport:用於方法,告訴clr該方法的實現位於指定dll的非托管代碼中。
有很多,大家應該都會用得到,在i這大家知道net框架類庫中有很多可用的定制特性即可,用的時候多總結慢慢就熟悉了。
3.2.定義自定義特性類
上面寫了一大通說特性怎么怎么樣,最后根據我們自己的需要定義自己的特性,應該是我們更高一些的需求,其實看net框架中的代碼,我們寫出自己定義的特性應該很容易。
創建並使用自定義特性包含四個步驟:
3.2.1.聲明自定義特性
[AttributeUsage(AttributeTargets.Class |AttributeTargets.Constructor |AttributeTargets.Field |AttributeTargets.Method |AttributeTargets.Property,AllowMultiple = true)] public class FlagInfoAttribute : System.Attribute
注:上面介紹過預定義特性所以也就沒什么可講的,大家應該知道attributeUsage定義的意思吧。所有的特性都是繼承system.attribute這個在這強調一下。
3.2.2.構建自定義特性
[AttributeUsage(AttributeTargets.Class |AttributeTargets.Constructor |AttributeTargets.Field |AttributeTargets.Method |AttributeTargets.Property,AllowMultiple = true)] public class FlagInfoAttribute : System.Attribute { string flagInfo=""; bool run=false;
public FlagInfoAttribute () { }
public FlagInfoAttribute (string flagInfo,bool run) { flaginfo=flaginfo; run=dosp; } public FlagInfoAttribute (string flagInfo) { flaginfo=flaginfo; } }
構造自定義特性其實和我們定義類基本一致,傳參只需在構造函數中定義即可。
3.2.3.在目標程序元素上應用自定義特性
特性的使用在方法上面“【】”標記使用即可
[flaginfo("測試用力",true)] piblic class falgDemo()
就是這么簡單,你是不是了解了特性這個知識點,應該是了解了,但是它是不是還可以更吊一些,前言我說過他叫過濾器,是的過濾器,只有了解到它過濾器的功能你才能真的發現它的厲害。
4.過濾器(Filter)
其實這一塊我在通過過濾器實現性能監控(含源碼)有用到,只是沒進行講解這個知識點而已。
過濾器的使用讓我們在代碼運行時的時候對其進行Action運行前,運行后和異常時進行可控性控制。至於例子看上面的博客就行。它解決了我在項目中一個難題,代碼很簡單,思路很不錯。
4.1.授權過濾器(Authorize)
4.1.1.默認授權過濾器Authorize
現在在網上無論是要求身份驗證的地方多得是,發郵件,買東西,在博客中下載資源。這里的某些操作,就是要經過驗證授權才被允許。在MVC中可以利用Authorize來實現。
[Authorize] public ActionResult Login() { return View(); }
如果使用此過濾器不得到認證,就算路由配置正確也是返回404錯誤。
4.1.2.自定義過濾器(繼承AuthorizeAttribute)
需要重寫底下兩個方法:
bool AuthorizeCore(HttpContextBase httpContext):這里主要是授權驗證的邏輯處理,返回true的則是通過授權,返回了false則不是。
void HandleUnauthorizedRequest(AuthorizationContext filterContext):這個方法是處理授權失敗的事情
public class MyAuthorizeAttribute:AuthorizeAttribute { protected override bool AuthorizeCore(HttpContextBase httpContext) { bool flag=false; //邏輯驗證 return flag; } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { filterContext.HttpContext.Response.Redirect("/Customer/Login"); } }
[MyAuthorize] public ActionResult show() { return View(); }
以上代碼經常用,很少用默認的。
4.2.處理錯誤過濾器(HandleError)
處理異常,是不是你第一個想到的是try catch,那么你就要在這里稍微換下思路了,因為有異常不一定就拋出,就算拋出是不是有一個專門的處理頁面顯示是不是更好一些。
4.2.1.默認HandleError
1.在使用此過濾器的時候,當有錯誤的時候他會默認跳轉到Views/Shared/Error視圖。
2.還要到web.config文件中的<system.web>一節中添加以下代碼
<customErrors mode="On" />
[HandleError(ExceptionType = typeof(Exception))] public ActionResult ThrowErrorLogin() { throw new Exception("發生了錯誤,親"); }
其中ExceptionType要處理的異常的類型,相當於Try/Catch語句塊里Catch捕捉的類型。
它的參數屬性還有:(F12查看即可)
View:指定需要展示異常信息的視圖,只需要視圖名稱就可以了,這個視圖文件要放在Views/Shared文件夾里面
Master:指定要使用的母版視圖的名稱
Order:指定過濾器被應用的順序
4.2.2自定義錯誤異常處理(繼承HandleErrorAttribute)
要重寫的方法void OnException(ExceptionContext filterContext)
對象ExceptionContext它的屬性:
ActionDescriptor:提供詳細的操作方法
Result:提供詳細的操作方法
Exception:未處理的異常
ExceptionHandled:如果這個異常處理完的話,就把它設為true,那么即使有其他的錯誤處理器捕獲到這個異常,也可以通過ExceptionHandler屬性判斷這個異常是否經過了處理,以免重復處理一個異常錯誤而引發新的問題。
4.3.緩存過濾器(OutputCache)
OutputCache過濾器用作緩存,節省用戶訪問應用程序的時間和資源,以提高用戶體驗。一般我都是直接做緩存處理,不怎么用這個過濾器。
4.4.自定義過濾器
OnActionExecuting執行前,OnActionExecuted執行后,OnResultExecuting返回結果前,OnResultExecuted返回方法后。
可以說自定義過濾器讓我們對我們方法整體進行時有了很好的控制的得到了保證。
public class MyCustomerFilterAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); filterContext.HttpContext.Response.Write(string.Format( "<br/> Action finish Execute.....")); } public override void OnActionExecuting(ActionExecutingContext filterContext) { CheckMessage(filterContext); filterContext.HttpContext.Response.Write(string.Format("<br/> Action start Execute.....")); base.OnActionExecuting(filterContext); } public override void OnResultExecuted(ResultExecutedContext filterContext) { filterContext.HttpContext.Response.Write(string.Format("<br/> Action finish Result.....")); base.OnResultExecuted(filterContext); } public override void OnResultExecuting(ResultExecutingContext filterContext) { filterContext.HttpContext.Response.Write(string.Format("<br/> Action start Execute.....")); base.OnResultExecuting(filterContext); } }
運用
[MyCustomerFilter] public ActionResult CustomerFilterTest() { Response.Write("<br/>正式方法執行"); return View(); }
運行一下看一下數序,這個在你理解后,我敢保證它將是你解決問題時一個全新的思路。