Home  >  Article  >  Backend Development  >  [ASP.NET MVC Mavericks Road] 02 - Summary of C# knowledge points

[ASP.NET MVC Mavericks Road] 02 - Summary of C# knowledge points

黄舟
黄舟Original
2016-12-30 13:48:431427browse

[ASP.NET
MVC Mavericks Road]02 - Summary of C# knowledge points

This blog post mainly provides a brief review of the C# language knowledge points that need to be mastered in asp.net mvc development. Especially some C# language features that are only available in C# 3.0. For those who are learning asp.net mvc, you might as well spend a few minutes browsing it. The C# knowledge points to be reviewed in this article include: properties, automatic properties, object collection initializers, extension methods, Lambda expressions and Linq queries. Senior C# "players" can stop by.

Contents of this article

1. Attributes(Attributes)

Attributes (Attributes), the definition of MSDN is: the common language runtime allows you to add description statements similar to keywords, Called attributes, it annotates elements in the program, such as types, fields, methods, properties, etc. Attributes are stored with the metadata of Microsoft .NET Framework files and can be used to describe your code to the runtime or affect the behavior of the application when the program is running.

For example, if the [Obsolete] attribute is marked before a method, VS will prompt a warning that the method has expired when the method is called, as shown below:

[ASP.NET MVC Mavericks Road] 02 - Summary of C# knowledge points

Another example, in the remote object of .Net Remoting, if you want to call or transfer an object, such as a class, or structure, the class or structure must be marked with the [Serializable] attribute. Also, a feature we use a lot when building XML Web services is [WebMegthod], which allows the return value of the public method of the HTTP request to be encoded into XML for delivery.

A characteristic is actually a class. The actual class name of the [Obsolete] characteristic is ObsoleteAttribute, but we do not need to add the Attribute suffix when annotating it. The system will automatically add it to us during name conversion.

The above mentioned are some of the features defined by the .NET system, of course there are many more. Understanding how to customize features will help us better use features in ASP.NET MVC programming, such as annotating attributes of the Model class with features to verify the legality of form inputs (described later).

Let's simulate a StringLenth feature often used by ASP.NET MVC, which is used to determine whether the user input exceeds the length limit. Let's simulate it now. First define a MyStringLenth attribute:

// 用户自定义的带有可选命名参数的 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; }
}

If the AttributeUsage restriction is not added above, the attribute can be declared in types (such as structures, classes, enumerations, delegates) and members (such as methods, fields, events, properties, indexes) the front.

Then we apply this feature to the following Order class:

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

Finally we look at how to use the MyStringLenth feature to verify the length of the user input string:

//检查成员字符串长度是否越限。
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();
}

The output effect is as follows:

[ASP.NET MVC Mavericks Road] 02 - Summary of C# knowledge points

2. Automatic properties

In C# 3.0 and later versions, when no other logic is needed in the accessor of the property, Automatically implemented properties make property declarations more concise.

The following example demonstrates the standard implementation and automatic implementation of properties:

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();
    }
}

Automatic properties can also have different access rights, such as:

public string Name { get;private set; }


Note that automatic properties cannot define read-only or write-only properties, and must provide both get and set accessors:

public string Name { get; }//编译出错
public string PetName { set; }//编译出错

3. Initializers for objects and collections

上面我们演示自动属性的时候给对象的实例初始化时是一个一个属性进行赋值的,有多少个属性就需要多少句代码。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();
    }
}

执行后输出如下结果:

[ASP.NET MVC Mavericks Road] 02 - Summary of C# knowledge points

5.Lambda 表达式

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

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

/// <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();

输出结果为:

[ASP.NET MVC Mavericks Road] 02 - Summary of C# knowledge points

上面我们使用的是委托和匿名函数来处理用户查询逻辑,并把它传递给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();

输出结果:

[ASP.NET MVC Mavericks Road] 02 - Summary of C# knowledge points

能熟练使用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();

输出结果为:

[ASP.NET MVC Mavericks Road] 02 - Summary of C# knowledge points

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

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

 以上就是[ASP.NET MVC 小牛之路]02 - C#知识点提要的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn