C# 3.0给我们带来了很多新特性,其中增加了很多“动态”内容,
让我们使用起来更加Sharp!
我在这里简单的介绍一下C# 3.0规范中的一些“新鲜”内容,小弟才疏学浅,望大家海涵!
Part1:使用隐含类型的本地变量
在C#3.0之前的C#语言中,我们在声明变量的时候都必须显式的指定变量类型(int,string之类的)
我们一般都是这样写的:
1static void InitAndPrint()
2{
3 int x = 7;
4 string s = "This is a string.";
5 double d = 99.99;
6 int[] numbers = new int[] { 0, 1, 2, 3, 4, 5, 6 };
7
8 Console.WriteLine("x: " + x);
9 Console.WriteLine("s: " + s);
10 Console.WriteLine("d: " + d);
11 Console.WriteLine("numbers: ");
12 foreach (int n in numbers) Console.WriteLine(n);
13}
而在C# 3.0中,我们这样写:
1static void InitAndPrint()
2 {
3 var x = 7;
4 var s = "This is a string.";
5 var d = 99.99;
6 var numbers = new int[] { 0, 1, 2, 3, 4, 5, 6 };
7
8 Console.WriteLine("x: " + x);
9 Console.WriteLine("s: " + s);
10 Console.WriteLine("d: " + d);
11 Console.WriteLine("numbers: ");
12 foreach (int n in numbers) Console.WriteLine(n);
13 }
大家应该可以注意到,原来的什么int、string之流都变成了var关键字了,甚至连数组的声明都不例外。
但是我们应该明白一点:
这种机制和Perl之类的var内心有着根本性的不同,虽然我们代码中写的是var,但是对于编译器来说,
通过变量的初始化表达式已经“正确的识别出了变量的类型”,所以在编译生成的MSIL中,我们是没有var类型出现的,
而是使用相应的数据类型来代替。
也就是说:
1、var标记的变量一定要初始化!!
2、对于集合类型的var在初始化的时候一定要指明类型(例如使用List<int>())
对于一些很长的,很复杂的变量声明来说,使用隐含类型声明可以有效的降低代码量,也避免了一些错误的出现。
Part2:使用扩展方法
扩展方法(Extension Method)
可以为已有的类型添加新的方法定义和实现,比如int类型目前没有一个名叫xxxyyy()的方法,
那么通过使用扩展方法,我们可以为int类型添加一个xxxyyy()方法。
这个有点类似于用来扩展系统功能的某些设计模式。
下面我们用代码来说话:
这是我们以前的写法:
1public static class Extensions
2{
3 public static string CamelCase(string identifier)
4{
5 string newString = "";
6 bool sawUnderscore = false;
7
8 foreach (char c in identifier)
9 {
10 if ((newString.Length == 0) && Char.IsLetter(c))
11 newString += Char.ToUpper(c);
12 else if (c == '_')
13 sawUnderscore = true;
14 else if (sawUnderscore)
15 {
16 newString += Char.ToUpper(c);
17 sawUnderscore = false;
18 }
19 else
20 newString += c;
21 }
22
23 return newString;
24}
25}
26
27static void Main(string[] args)
28{
29string[] identifiers = new string[] {
30 "do_something",
31 "find_all_objects",
32 "get_last_dict_entry"
33 };
34
35foreach (string s in identifiers)
36 Console.WriteLine("{0} becomes: {1}", s, Extensions.CamelCase(s));
37}
38
C# 3.0中我们可以这样写:
1public static class Extensions
2{
3 public static string CamelCase(this string identifier)
4{
5 string newString = "";
6 bool sawUnderscore = false;
7
8 foreach (char c in identifier)
9 {
10 if ((newString.Length == 0) && Char.IsLetter(c))
11 newString += Char.ToUpper(c);
12 else if (c == '_')
13 sawUnderscore = true;
14 else if (sawUnderscore)
15 {
16 newString += Char.ToUpper(c);
17 sawUnderscore = false;
18 }
19 else
20 newString += c;
21 }
22
23 return newString;
24}
25}
26
27static void Main(string[] args)
28{
29string[] identifiers = new string[] {
30 "do_something",
31 "find_all_objects",
32 "get_last_dict_entry"
33 };
34
35foreach (string s in identifiers)
36 Console.WriteLine("{0} becomes: {1}", s, Extensions.CamelCase(s));
37}
主要是下面这两个语句的变化:
1public static string CamelCase(this string identifier)
2Console.WriteLine("{0} becomes: {1}", s, s.CamelCase());
变量s原本是一个string类型,并没有CamelCase()方法,但是我们在CamelCase()方法的参数列表最前面加上一个this关键字,
则string s就拥有了一个新的方法CamelCase,很简单也很直接 :)
下面我们看一看一个稍微复杂一点的应用:
1public static class Extensions
2{
3public static List<T> Combine<T>(this List<T> a, List<T> b)
4{
5 var newList = new List<T>(a);
6 newList.AddRange(b);
7 return newList;
8}
9}
10
11static void Main(string[] args)
12{
13var odds = new List<int>();
14odds.Add(1);
15odds.Add(3);
16odds.Add(5);
17odds.Add(7);
18
19var evens = new List<int>();
20evens.Add(0);
21evens.Add(2);
22evens.Add(4);
23evens.Add(6);
24
25var both = odds.Combine(evens);
26Console.WriteLine("Contents of 'both' list:");
27foreach (int i in both)
28 Console.WriteLine(i);
29}
Part3:使用拉姆达表达式
拉姆达表达式(Lambda Expression)
可以算是一种匿名方法的实现吧。
在C# 2.0中引入了匿名方法的概念,我们可以写下如下代码:
1class Program
2{
3static void TestLambdaExpression()
4{
5 List<int> list = new List<int>();
6
7 list.Add(1);
8 list.Add(2);
9 list.Add(3);
10 list.Add(4);
11
12 List<int> evenNumbers = list.FindAll(delegate(int i) { return (i % 2) == 0; } );
13
14 foreach (int evenNumber in evenNumbers)
15 {
16 Console.WriteLine(evenNumber);
17 }
18}
19}
20
21static void Main(string[] args)
22{
23TestLambdaExpression();
24}
25
匿名方法避免了一些“极小函数”的出现,适当使用可以提高代码的可阅读性,显得清爽一点。
(List的FindAll方法需要一个Delegate参数,所以我们可以这样谢哈)
在C# 3.0中,我们可以这样写:
1static void TestLambdaExpression()
2{
3var list = new List<int>();
4
5 list.Add(1);
6 list.Add(2);
7 list.Add(3);
8 list.Add(4);
9
10var evenNumbers = list.FindAll(i => (i % 2) == 0);
11
12foreach (int evenNumber in evenNumbers)
13{
14 Console.WriteLine(evenNumber);
15 }
16}
其中i=>(i%2)==0就是我们说的Lambda Expression,在这里它就起到了原来匿名方法的作用,同时提升了代码的可阅读性。
下面是一个复杂一点的Lambda Expression的例子:
1fruit.FilterBy((string name, double price) => name == "banana" && price < 2.00);
看了这个例子应该可以自己动手写一些东西了吧(下楼买瓶可乐去也)
这里还有一个有趣的东西:Lambda Expression Tree(拉姆达表达式树)
这是一种用来在运行时把表达式作为数据的技术,我们可在运行时灵活的控制和改变表达式,
增强程序的灵活性!
还是看代码吧,这样直接一点:
1using System.Expressions;
2
3static void Main(string[] args)
4{
5 Expression<Func<int, bool>> filter = n => (n * 3) < 5;
6
7 BinaryExpression lt = (BinaryExpression) filter.Body;
8 BinaryExpression mult = (BinaryExpression) lt.Left;
9 ParameterExpression en = (ParameterExpression) mult.Left;
10 ConstantExpression three = (ConstantExpression) mult.Right;
11 ConstantExpression five = (ConstantExpression) lt.Right;
12
13 Console.WriteLine("({0} ({1} {2} {3}) {4})", lt.NodeType,
14 mult.NodeType, en.Name, three.Value, five.Value);
15}
输出:
(LT (Multiply n 3) 5)
通过这种技术,我们对于数据和数据表达式的操作可以变得更加轻松,不用动不动就是写方法了。
Part4:使用集合类型初始化器
集合类型初始化器(Collection Initializers)
想看一段“奇怪”的代码:
1class Program
2 {
3 static void Main(string[] args)
4 {
5 var a = new Point { x = 10, y = 13 };
6 var b = new Point { x = 33, y = 66 };
7
8 var r1 = new Rectangle { p1 = a, p2 = b };
9 Console.WriteLine("r1: p1 = {0},{1}, p2 = {2},{3}",
10 r1.p1.x, r1.p1.y, r1.p2.x, r1.p2.y);
11
12 var c = new Point { x = 13, y = 17 };
13 var r2 = new Rectangle { p2 = c };
14
15 Console.WriteLine("r2: p1 == {0}, p2 = {1}, {2}",
16 r2.p1, r2.p2.x, r2.p2.y);
17 }
18 }
19
20 public class Point
21 {
22 public int x, y;
23 }
24 public class Rectangle
25 {
26 public Point p1, p2;
27 }
注意到集合类型的初始化语法了吗?直截了当!
这也是C# 3.0语法规范中的一个新特性。
也许下面的例子更能说明问题:
这是我们以前的写法:
1class Program
2 {
3 private static List<string> keywords = new List<string>();
4
5 public static void InitKeywords()
6 {
7 keywords.Add("while");
8 keywords.Add("for");
9 keywords.Add("break");
10 keywords.Add("switch");
11 keywords.Add("new");
12 keywords.Add("if");
13 keywords.Add("else");
14 }
15
16 public static bool IsKeyword(string s)
17 {
18 return keywords.Contains(s);
19 }
20 static void Main(string[] args)
21 {
22 InitKeywords();
23 string[] toTest = { "some", "identifiers", "for", "testing" };
24
25 foreach (string s in toTest)
26 if (IsKeyword(s)) Console.WriteLine("'{0}' is a keyword", s);
27 }
28 }
这是我们在C# 3.0中的写法:
1class Program
2 {
3 private static List<string> keywords = new List<string> {
4 "while", "for", "break", "switch", "new", "if", "else"
5 };
6
7 public static bool IsKeyword(string s)
8 {
9 return keywords.Contains(s);
10 }
11
12 static void Main(string[] args)
13 {
14 string[] toTest = { "some", "identifiers", "for", "testing" };
15
16 foreach (string s in toTest)
17 if (IsKeyword(s)) Console.WriteLine("'{0}' is a keyword", s);
18 }
19 }是不是变得像枚举类型的初始化了?
个人觉得这对提高代码的阅读质量是很有帮助的,
否则一堆Add()看上去不简洁,感觉很啰嗦。
Part5:匿名类型
匿名类型(Anonymouse Type)——这年头什么多系都匿名了 : )
在初始化的时候根据初始化列表自动产生类型的一种机制。
典型的代码:
1class Program
2 {
3 static void Main(string[] args)
4 {
5 var x = new { a = 3, b = 5, c = "some text" };
6 Console.WriteLine(x.a.ToString());
7 }
8 }很奇怪吧~~~
不要认为这个var x真的是没有类型,其实这又是一个编译器的魔术,
当我们编译这段代码的时候,编译器会自动产生以下类型定义:
1class __Anonymous1
2{
3 private int _a = 3;
4 private int _b = 5;
5 private string _c = “some text”;
6
7 public int a { get { return _a; } set { _a = value; } }
8 public int b { get { return _b; } set { _b = value; } }
9 public int c { get { return _c; } set { _c = value; } }
10}
11
这样就不会和我前面所的var必须初始化,并且必须让编译器“猜得出”正确的类型这一句话产生矛盾。
PS:再过20年估计编译器就什么都可以做了,还是行业专家,“老板,买一个针对电信行业的编译器,要带SP增值服务扩张包的”
Part6:使用查询表达式
查询表达式(Query Expression)
大家都应该对SQL语句不陌生吧,在C# 2.0之前,嵌入到代码中的SQL就是下面这个样子:
1public void Test()
2{
3SqlConnection c = new SqlConnection(…);
4 c.Open();
5 SqlCommand cmd = new SqlCommand(
6 @“SELECT c.Name, c.Phone // queries in quotes
7 FROM Customers c
8 WHERE c.City = @p0”
9 );
10 cmd.Parameters[“@po”] = “London”; // arguments loosely bound
11 DataReader dr = c.Execute(cmd);
12 while (dr.Read()) {
13 string name = r.GetString(0);
14 string phone = r.GetString(1); // results loosely typed
15 DateTime date = r.GetDateTime(2); // compiler can’t help catch mistakes
16 }
17 r.Close();
18}
在C# 3.0中,我们可以将“SQL语句”方便的运用到其他地方,当然这里并不是真正的SQL语句~~
我觉得我会在以后的开发过程中使用很多以下的类似代码:
1class Program
2 {
3 static void Main(string[] args)
4 {
5 var contacts = new List<Contact>();
6
7 contacts.Add(new Contact("Michael", "520-331-2718",
8 "33140 SW Liverpool Lane", "WA"));
9 contacts.Add(new Contact("Jennifer", "503-998-1177",
10 "1245 NW Baypony Dr", "OR"));
11 contacts.Add(new Contact("Sean", "515-127-3340",
12 "55217 SW Estate Dr", "WA"));
13
14 var WAContacts =
15 from c in contacts
16 where c.State == "WA"
17 select new { c.Name, c.Phone };
18
19 Console.WriteLine("Contacts in the state of Washington: ");
20 foreach (var c in WAContacts)
21 {
22 Console.WriteLine("Name: {0}, Phone: {1}", c.Name, c.Phone);
23 }
24 }
25 }
26
27 class Contact
28 {
29 public string Name;
30 public string Phone;
31 public string Address;
32 public string State;
33
34 public Contact(string name, string phone, string address, string state)
35 {
36 this.Name = name;
37 this.Phone = phone;
38 this.Address = address;
39 this.State = state;
40 }
41 }
其中出现的代码:
1var WAContacts =
2 from c in contacts
3 where c.State == "WA"
4 select new { c.Name, c.Phone };
是否与我们熟悉的SQL语句有着极大的相似性呢?Of Course!
到底是SQL梦见了C#,还是C#梦见了SQL……