• 【Pro ASP.NET MVC 3 Framework】.学习笔记.1.主要语言特性


    C# 是一个富有特性的语言,并不是所有的程序员都熟悉本书依赖的所有特性。在本章,我们看看作为一个好的MVC程序员需要知道的C#特性。

    1 C#主要特性

    1 publicclass Product 2 { 3 publicstring ProductID { get; set; } 4 publicstring Name { get; set; } 5 publicdecimal Price { get; set; } 6 publicstring Catalog { get; set; } 7 }

    1.1 使用扩展方法

    扩展方法 在你不能拥有,和不能直接修改类时,给类添加方法一个方便的方式

    1 publicclass ShoppingCart 2 { 3 List<Product> Products { get; set; } 4 }

    假设我们需要决定ShoppingCart类中Product对象的所有值,但是我们不能修改类本身,也许是因为它来自于第三方,我们没有源代码。幸运地,我们可以使用扩展方法,来得到我们想要的功能。

    1 publicstaticclass MyExtensionMethods 2 { 3 publicstaticdecimal TotalPrice(this ShoppingCart cartParam) 4 { 5 decimal total =0; 6 foreach (Product prod in cartParam.Products) 7 { 8 total += prod.Price; 9 } 10 return total; 11 } 12 }

    第一个参数前的 this 关键字,使得 TotalPrices 成为一个扩展方法。第一个参数告诉 .net  要为哪个类扩展方法。我们的方法枚举Products,并返回 Product.Price属性的合计。

    扩展方法不能使你突破类为它的方法、fields 和属性定义的访问规则。你可以通过扩展方法扩展类的功能,但是只能访问你已经可以访问的类成员。

    1 staticvoid Main(string[] args) 2 { 3 ShoppingCart cart =new ShoppingCart 4 { 5 Products =new List<Product> { 6 new Product {Name ="Kayak", Price = 275M}, 7 new Product {Name ="Lifejacket", Price =48.95M}, 8 new Product {Name ="Soccer ball", Price =19.50M}, 9 new Product {Name ="Corner flag", Price =34.95M} 10 } 11 }; 12 13 decimal cartTotal = cart.TotalPrices(); 14 15 Console.WriteLine("Total: {0:c}", cartTotal); 16 Console.ReadKey(); 17 }

    1.1.1 将扩展方法应用到一个接口

    我们也可以创建扩展方法,将它应用到一个接口。它允许我们在任何安装启用接口的类上调用扩展方法。

    1 staticvoid Main(string[] args) 2 { 3 ShoppingCart cart =new ShoppingCart 4 { 5 Products =new List<Product> { 6 new Product {Name ="Kayak", Price = 275M}, 7 new Product {Name ="Lifejacket", Price =48.95M}, 8 new Product {Name ="Soccer ball", Price =19.50M}, 9 new Product {Name ="Corner flag", Price =34.95M} 10 } 11 }; 12 Product[] productArray = { 13 new Product {Name ="Kayak", Price = 275M}, 14 new Product {Name ="Lifejacket", Price =48.95M}, 15 new Product {Name ="Soccer ball", Price =19.50M}, 16 new Product {Name ="Corner flag", Price =34.95M} 17 }; 18 decimal cartTotal = cart.TotalPrices(); 19 decimal arrayTotal = productArray.TotalPrices(); 20 Console.WriteLine("Total: {0:c}", cartTotal); 21 Console.WriteLine("Total: {0:c}", arrayTotal); 22 Console.ReadKey(); 23 } 24 } 25 26 publicstaticclass MyExtensionMethods 27 { 28 publicstaticdecimal TotalPrices(this IEnumerable<Product> productEnum) 29 { 30 decimal total =0; 31 foreach (Product prod in productEnum) 32 { 33 total += prod.Price; 34 } 35 return total; 36 } 37 } 38 39 publicclass ShoppingCart:IEnumerable<Product>40 { 41 public List<Product> Products { get; set; } 42 43 public IEnumerator<Product> GetEnumerator() 44 { 45 return Products.GetEnumerator(); 46 } 47 IEnumerator IEnumerable.GetEnumerator() 48 { 49 return GetEnumerator(); 50 } 51 }

    第一个参数的类型已经变为 IEnumerabel<Product>,这意味着方法体中的foreach 循环会直接作用在参数对象上。另外,扩展方法没有变化。切换到接口,意味着我们能计算IEnumerable<Product>枚举的Products的合计值。

    1.1.2 创建一个过滤扩展方法

    扩展方法可以用来过滤对象的集合。一个操作IEnumerable<T>的扩展方法,会返回一个IEnumerable<T>,可以使用 yield 关键字 来对数据源中的 items 应用选择标准,产生一个缩小的结果集。

    1 ShoppingCart cart =new ShoppingCart 2 { 3 Products =new List<Product> { 4 new Product {Name ="Kayak",Catalog="Soccer", Price = 275M}, 5 new Product {Name ="Lifejacket", Price =48.95M}, 6 new Product {Name ="Soccer ball", Price =19.50M}, 7 new Product {Name ="Corner flag", Price =34.95M} 8 } 9 }; 10 11 foreach(Product prod in cart.FilterByCategory("Soccer")) 12 { 13 Console.WriteLine("Name: {0},Price {1:c}", prod.Name,prod.Price); 14 } 15 decimal total = cart.FilterByCategory("Soccer").TotalPrices(); 16 Console.WriteLine(total); 17 Console.ReadKey(); 18 19 publicstatic IEnumerable<Product> FilterByCategory(this IEnumerable<Product> productEnum,string categoryParam) 20 { 21 foreach(Product prod in productEnum) 22 { 23 if(prod.Catalog==categoryParam){ 24 yieldreturn prod; 25 } 26 } 27 }

    这个叫做 FilterByCategory的扩展方法,有一个附加的参数,允许我们在调用方法时,注入过滤项。那些 Category 属性 与 参数匹配的 Product 对象,会在结果 IEnumerable<Product>中返回,不匹配的会被丢弃。

    1.2 使用Lambda表达式

    我们可以使用委托,使得 FilterByCategory 方法更普遍。委托可以以我们选择的任何方式,被每个Product调用,以过滤对象。

    1 publicstatic IEnumerable<Product> Filter(this IEnumerable<Product> productEnum, Func<Product, bool> selectorParam) 2 { 3 foreach (Product prod in productEnum) 4 { 5 if (selectorParam(prod)) 6 { 7 yieldreturn prod; 8 } 9 } 10 }

    我们使用 Func 作为过滤参数,这意味着我们不用将委托定义为类型。Func 取走一个 Product 参数,并返回 bool。如果 Product包含在结果中,就返回 true。这个参数的另一端有点长。

    1 Func<Product, bool> categoryFilter =delegate(Product prod) 2 { 3 return prod.Category =="Watersports"; 4 }; 5 6 IEnumerable<Product> filteredProducts = cart.Filter(categoryFilter); 7 8 foreach (Product prod in filteredProducts) 9 { 10 Console.WriteLine("Name:{0},Price:{1:c}", prod.Name, prod.Price); 11 } 12 13 decimal total = filteredProducts.TotalPrices(); 14 15 Console.WriteLine(total); 16 Console.ReadKey();

    我们现在可以在委托中使用指定的标准,过滤 Product  对象。但是我们首先需要为每个标准定义 Func ,这样做不够理想。减少啰嗦的供选择的方式,是使用Lambda表达式,这是一种委托中方法体的简明表达方式。

    1 Func<Product, bool> categoryFilter = prod => prod.Category =="Soccer"; 2 IEnumerable<Product> filteredProducts = cart.Filter(categoryFilter); 3

    参数不用指定类型的方式被表达出来,它的类型会自动被推论。=> 读作 goes to ,将参数链接到 Lambda 表达式的结果。prod 将 Product 参数 goes to 到一个bool 结果,当 prod 的 Category 参数等于 true 时,返回 true。

    我们可以完全地取消 Func ,使得我们的语法更紧凑。

    1 IEnumerable<Product> filteredProducts = cart.Filter(prod=>prod.Category=="Soccer");

    我们将 Lambda 表达式作为参数提供给 Filter 方法,这是不错且原始的方法,来表达我们想要应用的过滤。

    1.2.1 Lambda 的其他格式

    1 prod=>prod.Category=="Soccer"||prod.Price<20

    我们不需要在Lambda表达式中表达委托的逻辑。我们可以简单地调用方法。如果我们需要一个 委托中有多个参数 的Lambda表达式,我们需要用括号将其包裹起来

    1 (prod, count) => prod.Price >20&& count >0

    如果表达式过于复杂,我们可以使用大括号

    1 (prod, count) => { 2 //...multiple code statements 3 return result; 4 }

    1.3 使用匿名类型

    var 关键字允许我们创建一个本地变量,而不用明确指定它的类型。结合对象初始化器和类型接口,我们能创建一个简单的 data-storage 对象,而不需要定义相应的类和结构。

    1 var myAnonType =new { 2 Name ="MVC", 3 Category ="Pattern"4 }; 5 6 Console.WriteLine("Name: {0}, Type: {1}", myAnonType.Name, myAnonType.Category);

    mAnonType 是一个匿名类型对象。它意味着编译器会自动地创建类型定义。强类型依然会实施。你你在它的属性被定义在初始化器后,只能 set 和 get 属性。

    C#编译器会根据初始化器中的参数的名字和类型生成类。两个拥有相同属性名字和类型的匿名类型对象,会被指派给相同的自动生成类。这意味着我们能用匿名类型对象创建一个数组。

    1.4 Linq

    想象偶们有一个 Product 对象集合,我们想要找到价格最高的前三个,并打印他们的名字和价格。一般我们会这样写:

    1 Product[] products = { 2 new Product{Name="Kayak",Category="Watersports",Price=275}, 3 new Product{Name="Soccer ball",Category="Soccer",Price=48.95M}, 4 new Product{Name="Soccer ss",Category="Soccer",Price=458.25M}, 5 new Product{Name="Soccer ss",Category="Soccer",Price=448.25M}, 6 new Product{Name="Soccer ss",Category="Soccer",Price=4.25M}, 7 new Product{Name="Soccer ss",Category="Soccer",Price=8.25M}, 8 new Product{Name="Soccer ss",Category="Soccer",Price=48.25M}, 9 new Product{Name="Soccer ss",Category="Soccer",Price=48.5M}, 10 new Product{Name="Soccer ss",Category="Soccer",Price=48.2M}, 11 new Product{Name="Soccer ss",Category="Soccer",Price=418.25M}, 12 new Product{Name="Soccer ss",Category="Soccer",Price=428.25M}, 13 new Product{Name="Soccer ss",Category="Soccer",Price=438.25M} 14 15 }; 16 17 Product[] results =new Product[3]; 18 Array.Sort(products, (item1, item2) =>19 { 20 return Comparer<decimal>.Default.Compare(item2.Price, item1.Price); 21 }); 22 Array.Copy(products, results, 3); 23 foreach (Product p in results) 24 { 25 Console.WriteLine("Item: {0},Cost: {1}", p.Name, p.Price); 26 } 27 Console.ReadKey();

    使用 Linq 的话:

    1 var results = from product in products 2 orderby product.Price descending 3 select new 4 { 5 product.Name, 6 product.Price 7 }; 8 int count =0; 9 foreach (var p in results) 10 { 11 Console.WriteLine("Item: {0},Cost: {1}", p.Name, p.Price); 12 if (++count ==3) 13 { 14 break; 15 } 16 } 17 Console.ReadKey();

    这看上去有一些整齐。使用 select 关键字,返回一个仅包含我们想要的属性的匿名类型。如果还要更强大的,可以使用 Linq 的 点符号。

    1 var results = products 2 .OrderByDescending(e => e.Price) 3 .Take(3) 4 .Select(e =>new { e.Name, e.Price }); 5 6 foreach (var p in results) 7 { 8 Console.WriteLine("Item: {0},Cost: {1}", p.Name, p.Price); 9 } 10 Console.ReadKey();

    OrderByDescending 方法将数据源中的 item 重新排列。在这个例子中,Lambda表达式返回我们想要用来比较的值。Take 方法返回结果集中,前几个指定数目的。Select 方法允许我们计划我们想要的结果。在这个例子中,我们计划一个匿名对象,包含名字和价格属性。下表中所有的Linq 方法都作用于 IEnumerable<T> 。

    扩展方法 描述 延迟
    All 如果所有数据源中的items匹配判断,返回true No
    Any 如果数据源中的Items至少有一个匹配判断,返回True No
    Contains 如果数据源包含一个指定的item或 value,返回 true No
    Count 返回数据源中 items 的个数 No
    First 返回第一个 No
    FirstOrDefault 如果数据源中没有 items,则返回默认值。如果有,返回第一个 No
    Last 返回最后一个 No
    LastOrDefault 如果数据源中没有items,则返回默认值。如果有,则返回最后一个 No
    Max Min 返回Lambda表达式指定项的最大最小值 No
    OrderBy  OrderByDescending 基于Lambda表达式返回的值,对数据源排序 Yes
    Reverse 逆转Items 的顺序 Yes
    Select 计划一个查询结果 Yes
    SelectMany 计划每个数据项到一个序列。链接所有的结果序列到一个单独序列 Yes
    Single 返回第一个item。如果有多个匹配,则抛出异常 No
    SingleOrDefault 返回第一个item。如果有多个匹配,则抛出异常。如果没有,则返回默认值。 No
    Skip  SkipWhile 跳过指定数目的元素,或当条件匹配时跳过 Yes
    Sum 判定选中的所有values的合计 No
    Take TakeWhile 从数据源开始处选择指定数目的元素。或者选择条件匹配的元素 Yes
    ToArray ToDictionary ToList 将数据源转换为一个数组,或其他集合类型 No
    Where 从数据源中过滤项目,不匹配判断 Yes

    1.4.1 延迟

    仅包含延期方法的查询,直到 IEnumerable<T> 结果中的 items 可枚举时才会执行。

    1 var results = products 2 .OrderByDescending(e => e.Price) 3 .Take(3) 4 .Select(e =>new { e.Name, e.Price }); 5 6 products[2] =new Product { Name ="Stadium", Price = 79500M }; 7 8 foreach (var p in results) 9 { 10 Console.WriteLine("Item: {0},Cost: {1}", p.Name, p.Price); 11 } 12 Console.ReadKey();

    我们先定义了查询语句后,修改了Product 数组中的对象。当枚举查询结果时,发现修改生效了。我们看到,查询没有被评估,直到结果被枚举。所以我们做的改变,被反射到输出中。

    1.4.2 使用延期前,需三思

    1 var results = products 2 .OrderByDescending(e => e.Price) 3 .Take(3) 4 .Select(e =>new { e.Name, e.Price }); 5 6 foreach (var p in results) 7 { 8 Console.WriteLine("Item: {0},Cost: {1}", p.Name, p.Price); 9 } 10 11 products[2] =new Product { Name ="Stadium", Price = 79500M }; 12 13 foreach (var p in results) 14 { 15 Console.WriteLine("Item: {0},Cost: {1}", p.Name, p.Price); 16 } 17 Console.ReadKey();

    我们会看到两次枚举的结果不一样。我们并不需要重新定义,更新,或任何其他方式修改Linq 查询。这意味着我们总是依赖延迟查询得到反射到最后更改过的数据源,也意味着查询的结果没有被缓存。如果你想要缓存查询的结果,你应该使用没有延迟的方法,如ToArray,它会立即执行。

    1.4.3 Linq 和 IQueryable<t> 接口

    Linq不仅能查询居住在内存中的C#对象,而且也能查询XML,非常方便地创建,处理,查询XML content。Parallel LINQ 可能回事下一代 Linq。

    Linq 查询也能在从EF获得的数据上执行。EF是 ORM 框架,它允许我们使用 c# 对象处理关系数据,它的机制会在后面看到。

    IQueryable<T> 接口由 IEnumerable<T> 派生而来。它用来标识查询执行的结果,针对一个特定的数据源。在我们的例子中,会是Sql Server db。这里没有必要直接使用IQueable<T>。Linq最好的特性之一,就是相同的查询可以被不同类型的数据源执行(objects,XML,db等)。

    2 理解 Razor 语法

    Razor是MVC3中新 view 引擎的名字。View 引擎处理 web 页面,寻找包含服务端介绍的指定的元素。

    Razor 声明以@开头。一个强类型的view,传递一个 model 对象到view。我们能通过 @Model 属性引用方法,字段,属性。

    在代码块中包含文本,以HTML 元素开头的,可以这样写

    1 @if (Model.Category =="Watersports") { 2 <p>@Model.Category <b>Splash!</b></p>3 }

    在代码块中包含文本,不以HTML元素开头的,应该这样写:

    1 @if (@Model.Category =="Watersports") { 2 @:Category: @Model.Category <b>Splash!</b>3 }

    如果想要添加一些不以 HTML 元素开头的行,可以使用 TEXT 元素,这样做和使用 @: 效果一样。

    1 <text>2 Category: @Model.Category <b>Splash!</b>3 <pre>4 Row, row, row your boat, 5 Gently down the stream... 6 </pre>7 </text>

    2.1 在代码块中包含多个功能

    1 @{ 2 if (Model.Category =="Watersports") { 3 @:Category: @Model.Category <b>Splash!</b>4 } 5 if (Model.Price >10) { 6 <h5>Pricey!</h5>7 } 8 }

    有两个 if 块,他们都相互独立的操作。这种类型的代码块,主要用来指定变量的值。

    2.2 用 View Bag 特性传递数据

    1 ViewBag.ProcessingTime = DateTime.Now.ToShortTimeString(); 2 @ViewBag.ProcessingTime

    ViewBag 是一个动态类型,意味着我们能用指定值的方式,定义他们的属性。使用ViewBag 和使用 ViewData 没有先进之分。

    2.3 使用布局

    当我们创建视图时,我们指定我们想要的布局,但是我们没有告诉 VS 使用哪一个。在新建View时,对话框告诉我们,如果在 Razor _viewstart文件中设置了此选项,则留空。在View 文件夹中,会看到 _ViewStart.cshtml 文件,该文件中写着 Layout=”~/Views/Shared/_Layout.cshtml”。

    View文件以 下划线开头,当用户直接请求该文件时,它不能返回给用户。

    当我们打开_Layout.cshtml文件,会看到以下代码:

    1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>@ViewBag.Title</title> 5 <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css"/> 6 <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" 7 type="text/javascript"></script> 8 </head> 9 10 <body>11 @RenderBody() 12 </body>13 </html>

    这时我们会理解Index.cshtml的这段代码:

    1 @{ 2 ViewBag.Title="Index"; 3 }

    它将该页的 title ,传递给了布局页。通过 @ViewBag.Title显示。Razor 使用 @RenderBody() 调用页面 body。

    2.4 不使用 Layouts

    Razor 布局只是一个选项,如果在创建 view 时没有选择layout选项,你会得到一个 html 模板。

    如果不没有 layout,view 必须包含所有必须的 content 。同时必须明确地设定 Layout 为 null。如果没有设置,view 会使用 _ViewStart.cshtml中指定的 layout。

  • 相关阅读:
    机器学习之logistic回归算法与代码实现原理
    机器学习之朴素贝叶斯算法原理与代码实现
    机器学习之KNN原理与代码实现
    Linux服务器LVM详细操作
    搭建nginx做文件下载服务器
    Django 知识点补充
    Nginx 代理TCP/UDP 端口
    Nginx 日志打印POST数据
    Linux 服务器基本优化
    Django FBV CBV以及使用django提供的API接口
  • 原文地址:https://www.cnblogs.com/msdynax/p/3271502.html
Copyright © 2020-2023  润新知