Heim > Artikel > Backend-Entwicklung > [ASP.NET MVC Mavericks Road] 02 – Zusammenfassung der C#-Wissenspunkte
[ASP.NET
MVC Mavericks Road] 02 – Zusammenfassung der C#-Wissenspunkte
Dieser Blog-Beitrag bietet hauptsächlich einen kurzen Überblick über die C#-Sprachwissenspunkte, die in asp.net beherrscht werden müssen MVC-Entwicklung. Insbesondere einige C#-Sprachfunktionen, die nur in C# 3.0 verfügbar sind. Für diejenigen, die asp.net mvc lernen, können Sie sich auch ein paar Minuten Zeit nehmen, um darin zu stöbern. Zu den C#-Wissenspunkten, die in diesem Artikel besprochen werden, gehören: Eigenschaften, automatische Eigenschaften, Objektsammlungsinitialisierer, Erweiterungsmethoden, Lambda-Ausdrücke und Linq-Abfragen. Ältere C#-„Spieler“ können vorbeischauen.
Inhalt dieses Artikels
1. Attribute
Attribute, die Definition von MSDN lautet: Mit der Common Language Runtime können Sie Beschreibungsanweisungen hinzufügen, die Schlüsselwörtern ähneln. Diese werden als Attribute bezeichnet und mit Anmerkungen versehen Elemente im Programm, wie Typen, Felder, Methoden, Attribute usw. Attribute werden mit den Metadaten von Microsoft .NET Framework-Dateien gespeichert und können verwendet werden, um Ihren Code zur Laufzeit zu beschreiben oder das Verhalten der Anwendung zu beeinflussen, wenn das Programm ausgeführt wird.
Wenn Sie beispielsweise das Attribut [Veraltet] vor einer Methode markieren, gibt VS beim Aufrufen der Methode eine Warnung aus, dass die Methode abgelaufen ist, wie unten gezeigt:
Ein weiteres Beispiel: Wenn Sie im Remote-Objekt von .Net Remoting ein Objekt wie eine Klasse oder Struktur aufrufen oder übertragen möchten, muss die Klasse oder Struktur mit dem Attribut [Serializable] gekennzeichnet sein. Eine Funktion, die wir beim Erstellen von XML-Webdiensten häufig verwenden, ist außerdem [WebMegthod], mit der der Rückgabewert der öffentlichen Methode der HTTP-Anfrage zur Übermittlung in XML codiert werden kann.
Ein Merkmal ist eigentlich eine Klasse. Der eigentliche Klassenname des [Obsolete] Merkmals ist ObsoleteAttribute, aber wir müssen das Attributsuffix beim Annotieren nicht hinzufügen. Das System fügt es uns automatisch hinzu Namensumwandlung.
Die oben genannten sind einige der durch das .NET-System definierten Funktionen, natürlich gibt es noch viele weitere. Wenn wir verstehen, wie Features angepasst werden, können wir Features in der ASP.NET MVC-Programmierung besser nutzen, z. B. das Annotieren von Attributen der Model-Klasse mit Features, um die Rechtmäßigkeit von Formulareingaben zu überprüfen (wird später beschrieben).
Simulieren wir eine StringLenth-Funktion, die häufig von ASP.NET MVC verwendet wird und mit der ermittelt wird, ob Benutzereingaben die Längenbeschränkung überschreiten. Lassen Sie es uns jetzt simulieren. Definieren Sie zunächst ein MyStringLenth-Attribut:
// 用户自定义的带有可选命名参数的 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; } }
Wenn die AttributeUsage-Einschränkung oben nicht hinzugefügt wurde, kann das Attribut in Typen (z. B. Strukturen, Klassen, Aufzählungen, Delegaten) und Mitgliedern (z. B. Methoden, Felder) deklariert werden , Ereignisse, Eigenschaften, Index).
Dann wenden wir diese Funktion auf die folgende Order-Klasse an:
// 应用自定义MyStringLenth特性于Order类的OrderID属性之上。MinLength和ErrorMessage是命名参数。 public class Order { [MyStringLenth("订单号", 6,MinLength = 3, ErrorMessage = "{0}的长度必须在{1}和{2}之间,请重新输入!")] public string OrderID { get; set; } }
Abschließend schauen wir uns an, wie wir die MyStringLenth-Funktion verwenden, um die Länge der Benutzereingabezeichenfolge zu überprüfen:
//检查成员字符串长度是否越限。 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(); }
Der Ausgabeeffekt ist wie folgt:
2. Automatische Attribute
In C# 3.0 und späteren Versionen, wenn der Accessor des Attributs nicht vorhanden ist erforderlich Bei Verwendung anderer Logik können automatisch implementierte Eigenschaften die Eigenschaftsdeklaration präziser gestalten.
Das folgende Beispiel demonstriert die Standardimplementierung und die automatische Implementierung von Eigenschaften:
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(); } }
Automatische Eigenschaften können auch unterschiedliche Zugriffsrechte haben, wie zum Beispiel:
public string Name { get;private set; }
Beachten Sie, dass automatische Eigenschaften keine schreibgeschützten oder schreibgeschützten Eigenschaften definieren können und sowohl Get- als auch Set-Accessoren bereitstellen müssen:
public string Name { get; }//编译出错 public string PetName { set; }//编译出错
3. Initialisierer für Objekte und Sammlungen
上面我们演示自动属性的时候给对象的实例初始化时是一个一个属性进行赋值的,有多少个属性就需要多少句代码。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)!