六:C#语言特性
自动实现的属性
C#的属性
public class Product { private string name; private string address; public string Name { get { return name; } set { name = value; } } public string Address { get { return address; } set { address = value; } } }
自动实现的属性
public class Product { public string Name { get; set; } public string Address { get; set; } }
这段代码的效果和上面的代码是一样的
对象和集合的初始化器
Product product = new Product(); product.Name = "MVC"; product.Address = "MVC"; Product product2 = new Product() { Name = "MVC", Address = "MVC" };
product 和product2的结果一样,但是product2初始化值的方法就称为初始化器,可读性更好。
拓展方法
对应不是自己拥有的类,或者不能直接修改的类,如果要添加方法,就需要使用拓展方法。
public static class MyExtensionClass { public static string MyExtensionMethod(this object o) { return "This is my Extension Method" + o.ToString(); } }
需要说明的几点
- 定义拓展方法的类修饰符必须是static
- 修饰拓展方法的修饰符也必须是static
- 参数的修饰符必须有this,参数类型是要拓展的类
定义了上述拓展方法后,在任何引用定义了该拓展类的命名空间的代码中,所有的对象都会多一个方法MyExtensionMethod。
接口拓展方法
定义了购物类实现了IEnumerable接口
public class ShoppingCart:IEnumerable<Product> { public List<Product> products { get; set; } public IEnumerator<Product> GetEnumerator() { return products.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } }
对接口IEnumerable<Product>进行拓展
public static class MyExtensionClass { public static decimal ProductListExtension(this IEnumerable<Product> products) { decimal total = 0; foreach(Product p in products) { total += p.Price; } return total; } }
那么在使用ShoppingCart类时,就可以直接使用该拓展方法
public string Test() { ShoppingCart cart = new ShoppingCart(); return cart.ProductListExtension().ToString(); }
过滤拓展方法
对集合进行过滤,是对IEnumerable<T>进行操作
新建一个和拓展方法2,这个拓展方法和上面的差别在于这个方法多了一个string类型的参数,用来过滤Name
public static decimal ProductListExtension2(this IEnumerable<Product> products,string NameFilter) { decimal total = 0; foreach (Product p in products) { if (p.Name.Contains(NameFilter)) { total += p.Price; } } return total; }
使用该拓展方法
public string Test() { ShoppingCart cart = new ShoppingCart(); return cart.ProductListExtension2("C#").ToString(); }
Lambda表达式
拓展方法的好处不言而喻,但是缺点是不够灵活,比如如果我新增了其他属性,过滤的不是Name而是新增属性。
那么上面的拓展方法就不能用了。
现在新建拓展方法3
public static decimal ProductListExtension3(this IEnumerable<Product> products, Func<Product,bool> selectParam) { decimal total = 0; foreach (Product p in products) { if (selectParam(p)) { total += p.Price; } } return total; }
使用该拓展方法
public string Test() { ShoppingCart cart = new ShoppingCart(); Func<Product,bool> NameFilter=delegate(Product p) { return p.Name.Contains("C#"); }; Func<Product, bool> PriceFilter = delegate(Product p) { return p.Price > 100; }; return cart.ProductListExtension3(PriceFilter).ToString(); }
可以看到这个拓展方法的通用性变的更好了。我们可以自定义过滤的条件,而拓展方法的接受是通用的。
但是还有一个问题在于定义Func还是太复杂。
public string Test4() { ShoppingCart cart = new ShoppingCart(); Func<Product,bool> NameFilter=delegate(Product p) { return p.Name.Contains("C#"); }; Func<Product, bool> PriceFilter = delegate(Product p) { return p.Price > 100; }; Func<Product, bool> PriceFilter2 = p => p.Price > 100; return cart.ProductListExtension3(PriceFilter2).ToString(); }
这里定义的PriceFilter2就是抛弃掉代理,而使用一种更简洁的语句,这就是Lambda表达式。
总结:Lambda就是委托更简洁的表达。
自动类型
C#允许定义一个局部变量而不指定变量类型。这称为类型推断,或隐式类型。
var MyProduct = new Product { Name="C#"};
匿名类型
C#允许创建一个数据存储对象,而不需要定义类或结构
var MyAnonProduct = new { MyName="C#",MyPrice=123}; decimal d = MyAnonProduct.MyPrice;
可以看到可以直接引用MyAnonProduct对象的内容。而实际上并没有定义一个类。
LINQ(执行语言集合查询)
public string Test() { Product[] products = { new Product{Name="Test",Address="111",Price=111}, new Product{Name="C#",Address="222",Price=222}, new Product{Name=".NET",Address="333",Price=333}, new Product{Name="MVC",Address="444",Price=444} }; var foundProducts = from p in products orderby p.Price descending where p.Name.Contains("C") select new { p.Name, p.Price }; return foundProducts.Count().ToString(); }
运行得到结果是2。其中计算变量foundProducts的表达式就是LINQ。这中表达式类似SQL。
还有一种另外一种拓展方法来使用LINQ。
var foundProducts2 = products.Where(item => item.Name.Contains("C")) .OrderByDescending(item=>item.Price);
效果和上面的一样。
延迟的LINQ查询。
public string Test() { Product[] products = { new Product{Name="Test",Address="111",Price=111}, new Product{Name="C#",Address="222",Price=222}, new Product{Name=".NET",Address="333",Price=333}, new Product{Name="MVC",Address="444",Price=444} }; var foundProducts2 = products.Where(item => item.Name.Contains("C")) .OrderByDescending(item=>item.Price); products[0].Name = "C++"; return foundProducts2.Count().ToString(); }
在执行了LINQ查询之后,按照正常理解这时候foundProducts2的数量应该是2。但是下面这条语句重新给第一个Product的Name赋值了C++。执行了这个方法的结果不是2而是3。也就证明了其实LINQ语句只是定义了查询语句,而没有执行查询。真正执行查询的是在最后一句。这就称为延迟查询。
但是并不是所有的LINQ语句都是延迟的。