基础拾遗
前言
本来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(); }
运行一下看一下数序,这个在你理解后,我敢保证它将是你解决问题时一个全新的思路。