所在的位置: Web开发 >> HTNL >> ASP.NET MVC-02-C#知识点

ASP.NET MVC-02-C#知识点

MVC-02-C#知识点

.特性(Attributes)特性(Attributes),MSDN的定义是:公共语言运行时允许你添加类似关键字的描写声明,叫做attributes,它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Framework文件的元数据保存在一起,可以用来向运行时描写你的代码,或在程序运行的时候影响应用程序的行动。例如,在一个方法前标注[Obsolete]特性,则调用该方法时VS则会提示该方法已过期的正告,如下图:

又如,在.NetRemoting的远程对象中,如果要调用或传递某个对象,例如类,或结构,则该类或结构则必须标注[Serializable]特性。还有,我们在构建XMLWeb服务时用得很多的一个特性就是[WebMegthod],它可让通过HTTP要求的公然方法的返回值编码成XML进行传递。

特性实际上就是一个类,[Obsolete]特性的实际类名是ObsoleteAttribute,但我们在标注的时候可以不带Attribute后缀,系统在名称转换时会自动给我们加上。

上面说的都是些.NET系统定义的一些特性,固然还有很多。了解如何自定义特性,有益有我们更好的在MVC编程使用特性,比如给Model类的属性标注特性来验证表单输入的合法性(以后进行介绍)。

下面我们来摹拟一个MVC常常要用到的StringLenth特性,它用于判断用户输入是不是超越长度限制。我们现在来摹拟它。先定义一个MyStringLenth特性:

//用户自定义的带有可选命名参数的MyStringLenthAttribute特性类。//该特性通过AttributeUsage限制它只能用在属性和字段上。[AttributeUsage(eld)]publicsealedclassMyStringLenthAttribute:Attribute{publicMyStringLenthAttribute(stringdisplayName,intmaxLength){xLength=maxLength;splayName=displayName;}//显示的名称,对外是只读的,所以不能通过可选参数来赋值,必须在构造函数中对其初始化。publicstringDisplayName{get;privateset;}//长度最大值,对外是只读的,所以不能通过可选参数来赋值,必须在构造函数中对其初始化。publicintMaxLength{get;privateset;}//错误信息,标注时可作为可选命名参数来使用。publicstringErrorMessage{get;set;}//长度最小值,标注时可作为可选命名参数来使用。publicintMinLength{get;set;}}

上面若不加AttributeUsage限制,特性可以声明在类型(如结构、类、枚举、拜托)和成员(如方法,字段,事件,属性,索引)的前面。

然后我们把这个特性运用在下面的Order类之上:

//运用自定义MyStringLenth特性于Order类的OrderID属性之上。MinLength和ErrorMessage是命名参数。publicclassOrder{[MyStringLenth("订单号",6,MinLength=3,ErrorMessage="{0}的长度必须在{}和{2}之间,请重新输入!")]publicstringOrderID{get;set;}}

最后我们看看如何使用MyStringLenth特性验证用户输入字符串的长度:

//检查成员字符串长度是不是越限。privatestaticboolIsMemberValid(intinputLength,MemberInfomember){foach(tCustomAttributes(true)){if(attributeisMyStringLenthAttribute){MyStringLenthAttributeattr=(MyStringLenthAttribute)attribute;stringdisplayName=splayName;intmaxLength=xLength;intminLength=nLength;stringmsg=rorMessage;if(inputLengthminLengthinputLengthmaxLength){iteLine(msg,displayName,minLength,maxLength);turnfalse;}else{turntrue;}}}turnfalse;}//验证输入是否合法privatestaticboolIsValid(Orderorder){if(order==null)turnfalse;foach(PropertyInfopintypeof(Order).GetProperties()){if(IsMemberValid(ngth,p))turntrue;}turnfalse;}publicstaticvoidMain(){stringinput=pty;Orderorder;do{iteLine("请输入订单号:");input=adLine();order=newOrder{OrderID=input};}while(!IsValid(order));iteLine("订单号输入正确,按任意键退出!");adKey();}

输出效果以下:

2.自动属性在C#3.0和更高版本中,当属性的访问器中不需要其他逻辑时,自动实现的属性可使属性声明更加简洁。

下面示例演示了属性的标准实现和自动实现:

classProgram{classPerson{//标准实现的属性int_age;publicintAge{get{turn_age;}set{if(value0value30){iteLine("设置的年龄有误!");turn;}_age=value;}}//自动实现的属性publicstringName{get;set;}}staticvoidMain(string[]args){Personp=newPerson();e=80;="小王";iteLine("{0}今年{}岁。",,e);adKey();}}

自动属性也可以有不同的访问权限,如:

publicstringName{get;privateset;}

注意,自动属性不能定义只读或只写的属性,必须同时提供get和set访问器:

publicstringName{get;}//编译出错publicstringPetName{set;}//编译出错

3.对象和集合的初始化器上面我们演示自动属性的时候给对象的实例初始化时是一个一个属性进行赋值的,有多少个属性就需要多少句代码。C#3.0和更高版本中有了对象集合初始化器,有了它,只需一句代码就可初始化一个对象或一个对象集合的所有属性。这在里先创建一个“商品”类,用于后面的示例演示:

///summary///商品类////summarypublicclassProduct{///summary///商品编号////summarypublicintProductID{get;set;}///summary///商品名称////summarypublicstringName{get;set;}///summary///商品描写////summarypublicstringDescription{get;set;}///summary///商品价格////summarypublicdecimalPrice{get;set;}///summary///商品分类////summarypublicstringCategory{set;get;}}

基于上面定义好的商品类,下面代码演示了如何通过初始化器来创建商品类的实例对象和集合:

staticvoidMain(string[]args){//对象初始化器的使用(可只给部份字段赋值)Productproduct=newProduct{ProductID=,Name="西瓜",Price=2.3M};//创建并初始化一个实例//集合初始化器的使用ListProductproList=newListProduct{newProduct{ProductID=,Name="西瓜",Price=2.3M},newProduct{ProductID=5,Name="苹果",Price=5.9M},newProduct{ProductID=,Name="樱桃",Price=4.6M}};//打印iteLine("对象初始化器:{0}{}{2}",oductID,,ice);foach(ProductpinproList){iteLine("集合初始化器:{0}{}{2}",oductID,,ice);}adKey();}

另外还有一些其它类型也可以使用初始化器,以下:

//数组使用初始化器string[]fruitArray={"apple","orange","plum"};//匿名类型使用初始化器varbooks=new{Title="MVC入门",Author="小王",Price=20};//字典类型使用初始化器Dictionarystring,intfruitDic=newDictionarystring,int(){{"apple",0},{"orange",20},{"plum",30}};

4.扩大方法扩大方法使您能够向现有类型“添加”方法,而无需创建新的派生类型或修改原始类型。扩大方法是一种特殊的静态方法,但可以像扩大类型上的实例方法一样进行调用。例如,我们可以让Random类的所有实例对象具有一个返回随机bool值的方法。我们不能对Random类本身进行修改,但可以对它进行扩大,以下代码所示:

staticclassProgram{///summary///随机返回true或false////summary///paramname="random"this参数自动指定到Random的实例/param///turns/turnspublicstaticboolNextBool(thisRandomrandom){xtDouble()0.5;}staticvoidMain(string[]args){//调用扩大方法Randomrd=newRandom();boolbl=xtBool();iteLine(String());adKey();}}

注意,扩大方法必须在非泛型的静态类中定义,上面的Program类如不加static修饰符则会报错。

我们可以创建一个接口的扩大方法,这样实现该接口的类都可以调用该扩大方法。看下面一个完全示例:

///summary///购物车类(实现IEnumerableProduct接口)////summarypublicclassShoppingCart:IEnumerableProduct{publicListProductProducts{get;set;}publicIEnumeratorProductGetEnumerator(){tEnumerator();}tEnumerator(){turnGetEnumerator();}}///summary///定义一个静态类,用于实现扩大方法(注意:扩大方法必须定义在静态类中)////summarypublicstaticclassMyExtensionMethods{///summary///计算商品总价钱////summarypublicstaticdecimalTotalPrices(thisIEnumerableProductproductEnum){decimaltotal=0;foach(ProductprodinproductEnum){total+=ice;}turntotal;}}classProgram{staticvoidMain(string[]args){//创建并初始化ShoppingCart实例,注入IEnumerableProductIEnumerableProductproducts=newShoppingCart{Products=newListProduct{newProduct{Name="Kayak",Price=},newProduct{Name="Lifejacket",Price=48.95M},newProduct{Name="Soccerball",Price=9.50M},newProduct{Name="Cornerflag",Price=34.95M}}};//创建并初始化一个普通的Product数组Product[]productArray={newProduct{Name="Kayak",Price=M},newProduct{Name="Lifejacket",Price=48.95M},newProduct{Name="Soccerball",Price=9.50M},newProduct{Name="Cornerflag",Price=34.95M}};//获得商品总价钱:用接口的方式调用TotalPrices扩大方法。decimalcartTotal=talPrices();//获得商品总价钱:用普通数组的方式调用TotalPrices扩大方法。decimalarrayTotal=talPrices();iteLine("CartTotal:{0:c}",cartTotal);iteLine("ArrayTotal:{0:c}",arrayTotal);adKey();}}

履行后输出以下结果:

mbda表达式Lambda表达式和匿名函数其实是一件事情。不同是,他们语法表现形式不同,Lambda表达式在语法上实际上就是匿名函数的简写。直接介绍匿名函数和Lambda表达式的用法没什么意思,在这里,我要根据实际运用来说一个二者用法的例子,这样在介绍知识点的同时也能和大家分享一下解决问题的思想。

假设我们要实现一个功能强大的商品查询方法,这个商品查询方法如何查询商品是可以由用户自己来决定的,用户可以根据价格来查询商品,也可以根据分类来查询商品等等,也就是说用户可以把自己的查询逻辑传递给这个查询方法。要编写这样一个方法,我们很自然的会想到用一个拜托来作为这个方法的参数,这个拜托就是用户处理商品查询的逻辑。我们不防把这个查询方法称为“商品查询器”。我们可以用静态的扩大方法来实现这个“商品查询器“,这样每一个商品集合对象(如IEnumerableProductproducts)可以直接调用该静态方法返回查询结果。解决问题的思想有了,接下来就是实现了。也许你对这一段描写有点蒙,结合代码可能让你更清晰。下面是这个“商品查询器”-Filter方法的实现代码:

///summary///定义一个静态类,用于实现扩大方法////summarypublicstaticclassMyExtensionMethods{///summary///商品查询器////summary///paramname="productEnum"扩大类型的实例援用/param///paramname="selectorParam"一个参数类型为Product,返回值为bool的拜托/param///turns查询结果/turnspublicstaticIEnumerableProductFilter(thisIEnumerableProductproductEnum,FuncProduct,boolselectorParam){foach(ProductprodinproductEnum){if(selectorParam(prod)){yieldturnprod;}}}}

没错,我们就是用这么简短的Filter方法来满足各种需求的查询。上面Product类使用的是前文定义的。这里也再一次见证了扩大方法的功效。为了演示Filter查询方法的调用,我们先来造一批数据:

staticvoidMain(string[]args){//创建商品集合IEnumerableProductproducts=newShoppingCart{Products=newListProduct{newProduct{Name="西瓜",Category="水果",Price=2.3M},newProduct{Name="苹果",Category="水果",Price=4.9M},newProduct{Name="MCV入门",Category="书籍",Price=9.5M},newProduct{Name="MCV提高",Category="书籍",Price=34.9M}}};}

接下来我们继续在上面Main方法中来调用查询方法Filter:

//用匿名函数定义一个具体的查询需求FuncProduct,boolfruitFilter=delegate(Productprod){tegory=="水果";};//调用Filter,查询分类为“水果”的商品IEnumerableProductfiltedProducts=lter(fruitFilter);//打印结果foach(ProductprodinfiltedProducts){iteLine("商品名称:{0},单价:{:c}",,ice);}adKey();

输出结果为:

上面我们使用的是拜托和匿名函数来处理用户查询逻辑,并把它传递给Filter方法,满足了前面所说的需求。但若使用Lambda表达式代替上面的匿名函数能使上面的代码看上去更简洁更人性化,以下代码所示:

FuncProduct,boolfruitFilter=prod=tegory=="水果";IEnumerableProductfiltedProducts=lter(fruitFilter);

没有了delegate关键字,没有了大小括号,看上去更舒服。固然上面两行代码可以继续简化为一行:

IEnumerableProductfiltedProducts=lter(prod=tegory=="水果");

这三种方式输出结果都是一样的。然后,我们还可以通过Lambda表达式实现各种需求的查询:

//查询分类为“水果”或单价大于30元的商品IEnumerableProductfiltedProducts=lter(prod=tegory=="水果"ice30);

通过这个示例,相信大家已清晰的了解并撑握了Lambda表达式的简单运用,而这就足够了:)。

NQ最后简单回顾一下LINQ。LINQ(LanguageIntegratedQuery语言集成查询)是VS和.NETFramework3.5版中一项突破性的创新,它在对象领域和数据领域之间架起了1座桥梁。

上面讲Lambda表达式时,用到的查询结果集的方式未免还是有点麻烦(由于自定义了一个Filter扩大方法),而Linq本身就集合了很多扩大方法,我们可以直接使用,大大的简化了编写查询代码的工作。例如,对这样一个数据集合:

Product[]products={newProduct{Name="西瓜",Category="水果",Price=2.3M},newProduct{Name="苹果",Category="水果",Price=4.9M},newProduct{Name="空心菜",Category="蔬菜",Price=2.2M},newProduct{Name="地瓜",Category="蔬菜",Price=.9M}};

如果要查询得到价钱最高的三个商品信息,如果不使用Linq,我们可能会先写一个排序方法,对products根据价钱由高到低进行排序,排序时需要创建一个新的Product[]对象用于存储排序好的数据。但用Linq可大大减少工作量,一两句代码就能搞定。以下代码所示,查出价钱最高的三个商品:

varsults=icedescendingselectnew{,ice};//打印价钱最高的三个商品intcount=0;foach(varpinsults){iteLine("商品:{0},价钱:{}",,ice);if(++count==3)bak;}adKey();

输出结果:

能熟练使用Linq是一件很爽的事情。上面的Linq语句和我们熟习的SQL查询语句类似,看上去非常整洁且易懂。但并不是每一种SQL查询语句在C#都有对应的关键字,有时候我们需要使用另外一种Linq查询方式,即“点号”方式的Linq查询方式,这类方式中的Linq查询方法都是扩大方法。如下面这段代码和上面实现的效果是一样的:

varsults=derByDescending(e=ice).Take(3).Select(e=new{,ice});foach(varpinsults){iteLine("商品:{0},价钱:{}",,ice);}adKey();

虽然类SQL的Linq查询方式比这类方式看上去更一目了然,但并不是每一种SQL查询语句在C#都有对应的关键字,比如这里的Take扩大方法就是类SQL的Linq查询语法没有的功能。

注意,有些Linq扩大方法分为“延后查询”(deferd)和“即时查询”(immediate)。延后查询意思是具有“延后查询”扩大方法的Linq语句只有当调用结果集对象的时候才开始真正履行查询,即时查询则是立即得到结果。比如上面的Linq语句的OrderByDescending扩大方法就是一个“延后查询”方法,当程序履行到Linq语句定义部份时并没有查询出结果并放到sults对象中,而是当程序履行到foach循环时才真正履行Linq查询语句得到查询结果。我们可以做个测试,在Ling语句以后,我们再将products[]对象重新赋值,以下代码所示:

varsults=derByDescending(e=ice).Take(3).Select(e=new{,ice});//在Linq语句以后对products[]重新赋值products[]=newProduct{Name="榴莲",Category="水果",Price=22.6M};//打印foach(varpinsults){iteLine("商品:{0},价钱:{}",,ice);}adKey();

输出结果为:

我们发现sults是重新赋值以后的结果。可想而知,查询语句是在sults被调用以后才真正履行的。

Linq非常强大也非常好用,这里只是把它当作一个学习MVC之前需掌握的知识点进行简单回顾。要灵活熟练地使用Linq还需要常常使用才行。

参考:《MVC3Framework》









































北京白癜风的治疗
治疗白癜风最好的办法



转载请注明:http://www.guyukameng.com/html/html1/770.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了