[ASP.NET
MVC Mavericks Road] 02 - C# 지식 포인트 요약
이 블로그 게시물은 주로 asp.net에서 마스터해야 할 C# 언어 지식 포인트에 대한 간략한 리뷰를 제공합니다. mvc 개발. 특히 C# 3.0에서만 사용할 수 있는 일부 C# 언어 기능입니다. asp.net mvc를 배우는 사람들이라면 그것을 탐색하는 데 몇 분 정도 시간을 할애할 수도 있습니다. 이 문서에서 검토할 C# 지식 포인트에는 속성, 자동 속성, 개체 컬렉션 이니셜라이저, 확장 메서드, Lambda 식 및 Linq 쿼리가 포함됩니다. 수석 C# "플레이어"가 들러도 됩니다.
이 문서의 내용
1. 속성
MSDN의 정의는 다음과 같습니다. 공용 언어 런타임을 사용하면 키워드와 유사한 설명 문을 추가할 수 있습니다. 유형, 필드, 메소드, 속성 등과 같은 프로그램의 요소 특성은 Microsoft .NET Framework 파일의 메타데이터와 함께 저장되며 런타임에 코드를 설명하거나 프로그램이 실행될 때 응용 프로그램의 동작에 영향을 미치는 데 사용할 수 있습니다.
예를 들어, 메서드 앞에 [Obsolete] 속성을 표시하면 VS는 아래와 같이 메서드를 호출할 때 메서드가 만료되었다는 경고 메시지를 표시합니다.
또 다른 예로, .Net Remoting의 원격 객체에서 클래스나 구조체 등의 객체를 호출하거나 전송하려면 해당 클래스나 구조체에 [Serialized] 속성을 표시해야 합니다. 또한 XML 웹 서비스를 구축할 때 우리가 많이 사용하는 기능은 [WebMegthod]인데, 이를 통해 HTTP 요청의 공개 메서드의 반환 값을 XML로 인코딩하여 전달할 수 있습니다.
특성은 실제로 클래스입니다. [Obsolete] 특성의 실제 클래스 이름은 ObsoleteAttribute이지만 주석을 추가할 때 시스템에서 자동으로 특성 접미사를 추가할 필요가 없습니다. 이름 변환.
위에 언급된 기능은 .NET 시스템에서 정의한 기능 중 일부이며, 물론 더 많은 기능이 있습니다. 기능을 사용자 정의하는 방법을 이해하면 양식 입력의 적법성을 확인하는 기능으로 Model 클래스의 특성에 주석을 추가하는 등 ASP.NET MVC 프로그래밍 기능을 더 잘 사용하는 데 도움이 됩니다(나중에 설명됨).
사용자 입력이 길이 제한을 초과하는지 확인하는 데 사용되는 ASP.NET MVC에서 자주 사용되는 StringLenth 기능을 시뮬레이션해 보겠습니다. 이제 시뮬레이션해 보겠습니다. 먼저 MyStringLenth 속성을 정의합니다.
// 用户自定义的带有可选命名参数的 MyStringLenthAttribute 特性类。 // 该特性通过AttributeUsage限制它只能用在属性和字段上。 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public sealed class MyStringLenthAttribute : Attribute { public MyStringLenthAttribute(string displayName, int maxLength) { this.MaxLength = maxLength; this.DisplayName = displayName; } //显示的名称,对外是只读的,所以不能通过可选参数来赋值,必须在构造函数中对其初始化。 public string DisplayName { get; private set; } //长度最大值,对外是只读的,所以不能通过可选参数来赋值,必须在构造函数中对其初始化。 public int MaxLength { get; private set; } //错误信息,标注时可作为可选命名参数来使用。 public string ErrorMessage { get; set; } //长度最小值,标注时可作为可选命名参数来使用。 public int MinLength { get; set; } }
위에 AttributeUsage 제한이 추가되지 않은 경우 속성은 유형(예: 구조, 클래스, 열거, 대리자) 및 멤버(예: 메서드, 필드)에서 선언될 수 있습니다. , 이벤트, 속성, 인덱스).
그런 다음 이 기능을 다음 Order 클래스에 적용합니다.
// 应用自定义MyStringLenth特性于Order类的OrderID属性之上。MinLength和ErrorMessage是命名参数。 public class Order { [MyStringLenth("订单号", 6,MinLength = 3, ErrorMessage = "{0}的长度必须在{1}和{2}之间,请重新输入!")] public string OrderID { get; set; } }
마지막으로 MyStringLenth 기능을 사용하여 사용자 입력 문자열의 길이를 확인하는 방법을 살펴보겠습니다.
//检查成员字符串长度是否越限。 private static bool IsMemberValid(int inputLength, MemberInfo member) { foreach (object attribute in member.GetCustomAttributes(true)) { if (attribute is MyStringLenthAttribute) { MyStringLenthAttribute attr=(MyStringLenthAttribute)attribute; string displayName = attr.DisplayName; int maxLength = attr.MaxLength; int minLength = attr.MinLength; string msg = attr.ErrorMessage; if (inputLength < minLength || inputLength > maxLength) { Console.WriteLine(msg, displayName, minLength, maxLength); return false; } else { return true; } } } return false; } //验证输入是否合法 private static bool IsValid(Order order) { if (order == null) return false; foreach (PropertyInfo p in typeof(Order).GetProperties()) { if (IsMemberValid(order.OrderID.Length, p)) return true; } return false; } public static void Main() { string input=string.Empty; Order order; do { Console.WriteLine("请输入订单号:"); input = Console.ReadLine(); order = new Order { OrderID = input }; } while (!IsValid(order)); Console.WriteLine("订单号输入正确,按任意键退出!"); Console.ReadKey(); }
출력 효과는 다음과 같습니다.
2. 자동 속성
C# 3.0 이상 버전에서는 속성의 접근자가 아닌 경우 필수 다른 논리를 사용하는 경우 자동으로 구현된 속성을 사용하면 속성 선언을 더 간결하게 만들 수 있습니다.
다음 예에서는 속성의 표준 구현과 자동 구현을 보여줍니다.
class Program { class Person { //标准实现的属性 int _age; public int Age { get { return _age; } set { if (value < 0 || value > 130) { Console.WriteLine("设置的年龄有误!"); return; } _age = value; } } //自动实现的属性 public string Name { get; set; } } static void Main(string[] args) { Person p = new Person(); p.Age = 180; p.Name = "小王"; Console.WriteLine("{0}今年{1}岁。",p.Name,p.Age); Console.ReadKey(); } }
자동 속성은 다음과 같은 다양한 액세스 권한을 가질 수도 있습니다.
public string Name { get;private set; }
자동 속성은 읽기 전용 또는 쓰기 전용 속성을 정의할 수 없으며 get 및 set 접근자를 모두 제공해야 합니다.
public string Name { get; }//编译出错 public string PetName { set; }//编译出错
3. 개체 및 컬렉션용 초기화 프로그램
上面我们演示自动属性的时候给对象的实例初始化时是一个一个属性进行赋值的,有多少个属性就需要多少句代码。C# 3.0和更高版本中有了对象集合初始化器,有了它,只需一句代码就可初始化一个对象或一个对象集合的所有属性。这在里先创建一个“商品”类,用于后面的示例演示:
/// <summary> /// 商品类 /// </summary> public class Product { /// <summary> /// 商品编号 /// </summary> public int ProductID { get; set; } /// <summary> /// 商品名称 /// </summary> public string Name { get; set; } /// <summary> /// 商品描述 /// </summary> public string Description { get; set; } /// <summary> /// 商品价格 /// </summary> public decimal Price { get; set; } /// <summary> /// 商品分类 /// </summary> public string Category { set; get; } }
基于上面定义好的商品类,下面代码演示了如何通过初始化器来创建商品类的实例对象和集合:
static void Main(string[] args) { //对象初始化器的使用 (可只给部分字段赋值) Product product = new Product { ProductID = 1234, Name = "西瓜", Price = 2.3M };//创建并初始化一个实例 //集合初始化器的使用 List<Product> proList = new List<Product> { new Product { ProductID = 1234, Name = "西瓜", Price = 2.3M }, new Product { ProductID = 2345, Name = "苹果", Price = 5.9M }, new Product { ProductID = 3456, Name = "樱桃", Price = 4.6M } }; //打印 Console.WriteLine("对象初始化器:{0} {1} {2}", product.ProductID, product.Name, product.Price); foreach (Product p in proList) { Console.WriteLine("集合初始化器:{0} {1} {2}", p.ProductID, p.Name, p.Price); } Console.ReadKey(); }
另外还有一些其它类型也可以使用初始化器,如下:
/数组使用初始化器 string[] fruitArray = {"apple","orange","plum" }; //匿名类型使用初始化器 var books = new { Title = "ASP.NET MVC 入门", Author = "小王", Price = 20 }; //字典类型使用初始化器 Dictionary<string, int> fruitDic = new Dictionary<string, int>() { { "apple", 10 }, { "orange", 20 }, { "plum", 30 } };
4.扩展方法
扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型或修改原始类型。扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。例如,我们可以让Random类的所有实例对象拥有一个返回随机bool值的方法。我们不能对Random类本身进行修改,但可以对它进行扩展,如下代码所示:
static class Program { /// <summary> /// 随机返回 true 或 false /// </summary> /// <param name="random">this参数自动指定到Random的实例</param> /// <returns></returns> public static bool NextBool(this Random random) { return random.NextDouble() > 0.5; } static void Main(string[] args) { //调用扩展方法 Random rd = new Random(); bool bl = rd.NextBool(); Console.WriteLine(bl.ToString()); Console.ReadKey(); } }
注意,扩展方法必须在非泛型的静态类中定义,上面的Program类如不加static修饰符则会报错。
我们可以创建一个接口的扩展方法,这样实现该接口的类都可以调用该扩展方法。看下面一个完整示例:
/// <summary> /// 购物车类 (实现 IEnumerable<Product> 接口) /// </summary> public class ShoppingCart : IEnumerable<Product> { public List<Product> Products { get; set; } public IEnumerator<Product> GetEnumerator() { return Products.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } /// <summary> /// 定义一个静态类,用于实现扩展方法(注意:扩展方法必须定义在静态类中) /// </summary> public static class MyExtensionMethods { /// <summary> /// 计算商品总价钱 /// </summary> public static decimal TotalPrices(this IEnumerable<Product> productEnum) { decimal total = 0; foreach (Product prod in productEnum) { total += prod.Price; } return total; } } class Program { static void Main(string[] args) { // 创建并初始化ShoppingCart实例,注入IEnumerable<Product> IEnumerable<Product> products = new ShoppingCart { Products = new List<Product> { new Product {Name = "Kayak", Price = 275}, new Product {Name = "Lifejacket", Price = 48.95M}, new Product {Name = "Soccer ball", Price = 19.50M}, new Product {Name = "Corner flag", Price = 34.95M} } }; // 创建并初始化一个普通的Product数组 Product[] productArray = { new Product {Name = "Kayak", Price = 275M}, new Product {Name = "Lifejacket", Price = 48.95M}, new Product {Name = "Soccer ball", Price = 19.50M}, new Product {Name = "Corner flag", Price = 34.95M} }; // 取得商品总价钱:用接口的方式调用TotalPrices扩展方法。 decimal cartTotal = products.TotalPrices(); // 取得商品总价钱:用普通数组的方式调用TotalPrices扩展方法。 decimal arrayTotal = productArray.TotalPrices(); Console.WriteLine("Cart Total: {0:c}", cartTotal); Console.WriteLine("Array Total: {0:c}", arrayTotal); Console.ReadKey(); } }
执行后输出如下结果:
5.Lambda 表达式
Lambda 表达式和匿名函数其实是一件事情。不同是,他们语法表现形式不同,Lambda 表达式在语法上实际上就是匿名函数的简写。直接介绍匿名函数和Lambda表达式的用法没什么意思,在这里,我要根据实际应用来讲一个两者用法的例子,这样在介绍知识点的同时也能和大家分享一下解决问题的思想。
假如我们要实现一个功能强大的商品查询方法,这个商品查询方法如何查询商品是可以由用户自己来决定的,用户可以根据价格来查询商品,也可以根据分类来查询商品等等,也就是说用户可以把自己的查询逻辑传递给这个查询方法。要编写这样一个方法,我们很自然的会想到用一个委托来作为这个方法的参数,这个委托就是用户处理商品查询的逻辑。 我们不防把这个查询方法称为“商品查询器”。我们可以用静态的扩展方法来实现这个“商品查询器“,这样每个商品集合对象(如 IEnumerable
/// <summary> /// 定义一个静态类,用于实现扩展方法 /// </summary> public static class MyExtensionMethods { /// <summary> /// 商品查询器 /// </summary> /// <param name="productEnum">扩展类型的实例引用</param> /// <param name="selectorParam">一个参数类型为Product,返回值为bool的委托</param> /// <returns>查询结果</returns> public static IEnumerable<Product> Filter(this IEnumerable<Product> productEnum, Func<Product, bool> selectorParam) { foreach (Product prod in productEnum) { if (selectorParam(prod)) { yield return prod; } } } }
没错,我们就是用这么简短的Filter方法来满足各种需求的查询。上面Product类使用的是前文定义的。这里也再一次见证了扩展方法的功效。为了演示Filter查询方法的调用,我们先来造一批数据:
static void Main(string[] args) { // 创建商品集合 IEnumerable<Product> products = new ShoppingCart { Products = new List<Product> { new Product {Name = "西瓜", Category = "水果", Price = 2.3M}, new Product {Name = "苹果", Category = "水果", Price = 4.9M}, new Product {Name = "ASP.NET MCV 入门", Category = "书籍", Price = 19.5M}, new Product {Name = "ASP.NET MCV 提高", Category = "书籍", Price = 34.9M} } }; }
接下来我们继续在上面Main方法中来调用查询方法Filter:
//用匿名函数定义一个具体的查询需求 Func<Product, bool> fruitFilter = delegate(Product prod) { return prod.Category == "水果"; }; //调用Filter,查询分类为“水果”的商品 IEnumerable<Product> filteredProducts = products.Filter(fruitFilter); //打印结果 foreach (Product prod in filteredProducts) { Console.WriteLine("商品名称: {0}, 单价: {1:c}", prod.Name, prod.Price); } Console.ReadKey();
输出结果为:
上面我们使用的是委托和匿名函数来处理用户查询逻辑,并把它传递给Filter方法,满足了前面所说的需求。但若使用Lambda表达式代替上面的匿名函数能使上面的代码看上去更简洁更人性化,如下代码所示:
Func<Product, bool> fruitFilter = prod => prod.Category == "水果"; IEnumerable<Product> filteredProducts = products.Filter(fruitFilter);
没有了delegate关键字,没有了大小括号,看上去更舒服。当然上面两行代码可以继续简化为一行:
IEnumerable<Product> filteredProducts = products.Filter(prod => prod.Category == "水果");
这三种方式输出结果都是一样的。然后,我们还可以通过Lambda表达式实现各种需求的查询:
//查询分类为“水果”或者单价大于30元的商品 IEnumerable<Product> filteredProducts = products.Filter(prod => prod.Category == "水果" || prod.Price > 30 );
通过这个示例,相信大家已经清晰的了解并撑握了Lambda表达式的简单应用,而这就足够了:)。
6.LINQ
最后简单回顾一下LINQ。LINQ(Language Integrated Query语言集成查询)是 VS 2008 和 .NET Framework 3.5 版中一项突破性的创新,它在对象领域和数据领域之间架起了一座桥梁。
上面讲Lambda表达式时,用到的查询结果集的方式未免还是有点麻烦(因为自定义了一个Filter扩展方法),而Linq本身就集合了很多扩展方法,我们可以直接使用,大大的简化了编写查询代码的工作。例如,对于这样一个数据集合:
Product[] products = { new Product {Name = "西瓜", Category = "水果", Price = 2.3M}, new Product {Name = "苹果", Category = "水果", Price = 4.9M}, new Product {Name = "空心菜", Category = "蔬菜", Price = 2.2M}, new Product {Name = "地瓜", Category = "蔬菜", Price = 1.9M} };
如果要查询得到价钱最高的三个商品信息,如果不使用Linq,我们可能会先写一个排序方法,对products根据价钱由高到低进行排序,排序时需要创建一个新的Product[]对象用于存储排序好的数据。但用Linq可大大减少工作量,一两句代码就能搞定。如下代码所示,查出价钱最高的三个商品:
var results = from product in products orderby product.Price descending select new { product.Name, product.Price }; //打印价钱最高的三个商品 int count = 0; foreach (var p in results) { Console.WriteLine("商品:{0},价钱:{1}", p.Name, p.Price); if (++count == 3) break; } Console.ReadKey();
输出结果:
能熟练使用Linq是一件很爽的事情。上面的Linq语句和我们熟悉的SQL查询语句类似,看上去非常整洁且易懂。但并不是每一种SQL查询语句在C#都有对应的关键字,有时候我们需要使用另外一种Linq查询方式,即“点号”方式的Linq查询方式,这种方式中的Linq查询方法都是扩展方法。如下面这段代码和上面实现的效果是一样的:
var results = products .OrderByDescending(e => e.Price) .Take(3) .Select(e => new { e.Name,e.Price}); foreach (var p in results) { Console.WriteLine("商品:{0},价钱:{1}", p.Name, p.Price); } Console.ReadKey();
虽然类SQL的Linq查询方式比这种方式看上去更一目了然,但并不是每一种SQL查询语句在C#都有对应的关键字,比如这里的Take扩展方法就是类SQL的Linq查询语法没有的功能。
注意,有些Linq扩展方法分为“延后查询”(deferred)和“即时查询”(immediate)。延后查询意思是拥有“延后查询”扩展方法的Linq语句只有当调用结果集对象的时候才开始真正执行查询,即时查询则是立即得到结果。比如上面的Linq语句的OrderByDescending扩展方法就是一个“延后查询”方法,当程序执行到Linq语句定义部分时并没有查询出结果并放到results对象中,而是当程序执行到foreach循环时才真正执行Linq查询语句得到查询结果。我们可以做个测试,在Ling语句之后,我们再将products[1]对象重新赋值,如下代码所示:
var results = products .OrderByDescending(e => e.Price) .Take(3) .Select(e => new { e.Name, e.Price }); //在Linq语句之后对products[1]重新赋值 products[1] = new Product { Name = "榴莲", Category = "水果", Price = 22.6M }; //打印 foreach (var p in results) { Console.WriteLine("商品:{0},价钱:{1}", p.Name, p.Price); } Console.ReadKey();
输出结果为:
我们发现results是重新赋值之后的结果。可想而知,查询语句是在results被调用之后才真正执行的。
Linq非常强大也非常好用,这里只是把它当作一个学习ASP.NET MVC之前需掌握的知识点进行简单回顾。要灵活熟练地使用Linq还需要经常使用才行。
以上就是[ASP.NET MVC 小牛之路]02 - C#知识点提要的内容,更多相关内容请关注PHP中文网(www.php.cn)!