代码改变世界

【收藏】C# 2.0&3.0新特性总结

2008-04-23 11:06  Jacky_Xu  阅读(392)  评论(0编辑  收藏  举报

c#2.0新特性

范型

    
我们知道通用的数据结构可以采用object存储任何数据类型。使用object问题是:

  • 显示的强制转带来的代码复杂性
  • 换装箱拆箱的性能损失(为什么有性能损失?因为涉及动态内存分配和运行时类型检查)。还有一些运行时才会出现的类型转换异常也是我们难以在代码编写的时候能够检查到的,防不胜防。

    范型应时而生,它的思路是什么呢?它接受带有类型参数并存储这个类型而不转换它,类型参数在类名字后的<T>中指定。T相当于一个占位符,直到使用的时候才指定一个实际的类型。确切的说当应用程序首次创建一个构造范型类型的实例时,.net公共语言运行时的实时编译器JIT将在进程中把范型IL和元数据转化为本地代码并把类型参数转化为实际的类型。对于这个泛型类型的后续引用将会使用相同的本机代码。这也就是传说中的范型类型实例化。

看一段代码:

范型的一个有趣话题:约束

 

问题溯源:由于T代表的可能是任何类型,所以我们使用T的方法仅限于Equals GetHasCode ToString,那么我们要使用某些特定数据类型的方法呢?比如实现了IComparable接口的数据类型的CompareTo方法?

1 

一种方法是强制转换到IComparable接口。这种方法的缺点是:1.进行运行时动态类型检查增加了性能上的开销,2.自然地如果key没有实现IComparable接口的异常报告推迟到了运行时


另一种方法就是约束列表,关键词是
where,后面跟的是类或者接口的列表,还有一个可选、特殊的new()约束;其实就是说这个类型必须要有一个公开无参构造函数,这就允许泛型类使用这种构造函数来创建实例。约束是一把双刃剑,一方面它提供了编译时类型检查,增强了性能,但是它也限制了范型类型的能力。
2 

范型方法
范型方法在方法名字后面使用
<>指定一个或者多个类型参数,类型参数可以出现在参数列表,返回类型和方法体内。编译器会使用一种类型推断的机制通过其他参数类推断正确的类型参数。

给出一些范型的例子:


  1 public class Test<T>
  2    {
  3        public T item getset; }
  4
  5        public void Display()
  6        {
  7            Console.WriteLine(item.ToString());
  8        }

  9
 10    }

 11
 12    public class Map<K, V>
 13    {
 14        Dictionary<K, V> d = new Dictionary<K, V>();
 15        public void Add(K key, V value)
 16        {
 17
 18            d.Add(key, value);
 19        }

 20        public void Display()
 21        {
 22            foreach (var item in d.Keys)
 23            {
 24                Console.WriteLine("Key:" + item + " Value:" + d[item]);
 25            }

 26        }

 27    }

 28
 29    public class ComparableTest<T> where T : IComparable
 30    {
 31        public T item getset; }
 32
 33        public void Display()
 34        {
 35            if (item.CompareTo(10> 0)
 36            {
 37                Console.WriteLine(item.ToString() + ">" + "10");
 38            }

 39            else
 40            {
 41                Console.WriteLine(item.ToString() + "<" + "10");
 42            }

 43        }

 44
 45    }

 46
 47    public class MapWithConstraint<K, V> where V : new()
 48    {
 49        Dictionary<K, V> d = new Dictionary<K, V>();
 50        public void Add(K key, V value)
 51        {
 52
 53            d.Add(key, value);
 54        }

 55        public void Display()
 56        {
 57            foreach (var item in d.Keys)
 58            {
 59                Console.WriteLine("Key:" + item + " Value:" + d[item]);
 60            }

 61        }

 62    }

 63
 64    public class GenericMethodTest
 65    {
 66        public T  Display<T, V, K>(T t, K k, V v)
 67        {
 68            Console.WriteLine(t.ToString() + "  " + k.ToString() + " " + v.ToString());
 69            return t ;
 70        }

 71    }

 72    public class Student
 73    {
 74        public int ID getset; }
 75        public string Name getset; }
 76    }

 77 class Program
 78    {
 79        static void Main(string[] args)
 80        {
 81            Test<int> t = new Test<int>();
 82            t.item = 10;
 83            t.Display();
 84
 85
 86            Map<stringint> map = new Map<stringint>();
 87            map.Add("King"23);
 88            map.Add("XiaoQiang"24);
 89            map.Display();
 90
 91            ComparableTest<int > test = new ComparableTest<int >();
 92            test.item = 123;
 93             test.Display();
 94            
 95
 96           // ComparableTest<Student > test2 = new ComparableTest<Student >();
 97
 98
 99            GenericMethodTest g = new GenericMethodTest();
100            Console.WriteLine(g.Display(233252));
101
102
103
104            Console.ReadLine();
105            
106        }

107    }

108

匿名方法

匿名方法其实就是体现了这样一个原则:如无必要,勿增实体;我们在一个简单的WinForm环境中来说明这个问题:一个按钮单击事件我们可以这样来定义响应代码。

 1   this.button1.Click += delegate
 2            {
 3                this.label1.Text = "King2002";
 4            }
;
 5            
 6            this.button1.Click += delegate(object sender, System.EventArgs arg)
 7            {
 8                this.label1.Text = "Test";
 9            }
;
10            this.button1.Click += new System.EventHandler(this.button1_Click);
11 private void button1_Click(object sender, EventArgs e)
12        {
13            MessageBox.Show("Hello");
14        }

15
16
17

匿名方法使得代码对于委托的实现更加简单,匿名方法还有一个用途就是操作一些私有成员,因为它相当于共享了一部分代码。

这里会有一个疑问:匿名方法和委托类型的隐式转换有什么要求?答案:只要参数列表和委托类型的返回值是兼容的就可以完成转换。

  •  参数列表兼容:无参数或者参数的数量、类型、修饰符严格匹配
  • 无返回类型,所有return语句形管的表达式可以被隐式转换到委托的类型

后面我们会看到更简单使用delegate的例子。

 

迭代器

 一个对象如果是可枚举的,那么我们可以使用foreach语句来遍历其中的元素。实际上是调用了这个对象的GetEnumberator方法,它返回一个enumberator(枚举器)。实现枚举器很难但是我们可以使用迭代器实现!

 1 public class DaysOfTheWeek : System.Collections.IEnumerable
 2    {
 3        string[] m_Days = "Sun""Mon""Tue""Wed""Thr""Fri""Sat" };
 4
 5        public System.Collections.IEnumerator GetEnumerator()
 6        {
 7            for (int i = 0; i < m_Days.Length; i++)
 8            {
 9                yield return m_Days[i];
10            }

11        }

12    }

13
14    class TestDaysOfTheWeek
15    {
16        static void Main()
17        {
18            // Create an instance of the collection class
19            DaysOfTheWeek week = new DaysOfTheWeek();
20
21            // Iterate with foreach
22            foreach (string day in week)
23            {
24                System.Console.Write(day + " ");
25            }

26        }

27    }

28

上面是MSDN上的一个简单的例子,有了直观的印象我们可以深究一点:

迭代器是产生值的有序序列的一个语句块,迭代器不是一种成员,它只是实现函数成员的方式。Yield return语句产生迭代的下一个值 yield break 语句指明迭代已经完成;GetEnumerator返回值只要是枚举器接口或者是可枚举接口(System.Collections.IEnumerable   System.Collections.IEnumerator System.Collections.Generic.IEnumerable<T> System.Collections.Generic.IEnumerator<T> ),迭代器就可以被用做函数体。

 

不完整类型
不完整类型完全是为了更好的进行代码管理。仔细观察我们现在添加一个页面时,它的后代代码就使用了不完整类型:

public partial class _Default : System.Web.UI.Page 

 


c#3.0新特性


    自动实现属性
    Auto-Implemented Properties

       

     1public class Card
     2
     3    {
     4
     5        //这样的属性是不是简单多了?
     6
     7        public int ID getset; }
     8
     9        public string Password getset; }
    10
    11    }

    12
    13    class Customer
    14
    15    {
    16
    17        public double TotalPurchases getset; }
    18
    19        public string Name getprivate set; } // read-only
    20
    21        public int CustomerID getprivate set; } // read-only
    22
    23    }

    24

    3 

     

    • 自动属性必须包含get set,如果是只读的就添加private关键字
    • Attributes 不允许使用自动属性,这里还是推荐使用常规的属性书写方式

    <ms-help://MS.MSDNQTR.v90.en/dv_csref/html/aa55fa97-ccec-431f-b5e9-5ac789fd32b7.htm>
     


    检索表达式

    Query Expressions

    LINQ毫无疑问是c#3.0最抢眼的东西,关于LINQ的文章已经很多,这里不展开,详细资料请参考:

    <ms-help://MS.MSDNQTR.v90.en/dv_csref/html/40638f19-fb46-4d26-a2d9-a383b48f5ed4.htm>

     

    int[] items = new int[] 12345 };

                IEnumerable
    <int> ints = from item in items

                                        
    where item > 2.5

                                        select item;

                
    foreach (var p in ints)

                
    {

                    Console.WriteLine(p);

                }
     

     

    隐式类型变量
    Implicitly Typed Variables (var)

        内部变量的类型可以使用var而不是确切的类型。var关键字可以指示编译器通过右侧的初始化部分来推断实际的数据类型。

     

                        

      var num = 5;

                var a 
    = new[] 2567 };

                
    // anon 被编译成匿名类型 注意下面的Name Age都没有定义

                var anon 
    = new { Name = "Terry", Age = 34 };

                var list 
    = new List<int>();

                
    using (var file = new System.IO.StreamReader(@"D:\http.txt", Encoding.Default))

                
    {

                    Console.WriteLine(file.ReadLine());

                    Console.WriteLine(anon.Name );

                    Console.WriteLine(anon.Age );

                }


    var的使用会有一些约束:<ms-help://MS.MSDNQTR.v90.en/dv_csref/html/b9218fb2-ef5d-4814-8a8e-2bc29b0bbc9b.htm>

     

    对象、集合初始化

    Object and Collection Initializers


    设计完成之后生成了一些类代码,但是这里有一个问题就是属性代码的填写,在FX3.0属性代码的已经简化,

    类的初始化也提供了更简单的书写方式;可以看一下下面代码段中c2的初始化,列表对象的初始化按照同样的格式也会大大的简化。

     

     1public class Card
     2
     3    {
     4
     5        //这样的属性是不是简单多了?
     6
     7        public int ID getset; }
     8
     9        public string Password getset; }
    10
    11    }

    12
    13    class Program
    14
    15    {
    16
    17        static void Main(string[] args)
    18
    19        {
    20
    21            //通常我们初始化一个对象是这样做的
    22
    23            Card c = new Card();
    24
    25            c.ID = 2002;
    26
    27            c.Password = "henghenghaxi";
    28
    29
    30
    31            //现在我们可以这样做
    32
    33            Card c2 = new Card { ID = 23, Password = "testnum" };
    34
    35
    36
    37            Console.WriteLine(c.ID.ToString() + " - " + c.Password);
    38
    39            
    40
    41            //列表的初始化
    42
    43            List<Card> cards = new List<Card>
    44
    45            {
    46
    47                new Card{ID=12,Password="12sfdgr"},
    48
    49                new Card{ID=13,Password="12sdfwsd"},
    50
    51                new Card{ID=14,Password="12jkhjh"},
    52
    53                new Card{ID=15,Password="1dfsed2"},
    54
    55                new Card{ID=16,Password="1sdfsd2"},
    56
    57
    58
    59            }
    ;
    60
    61                         //看下面的例子
    62
    63            List<int> digits = new List<int> 0123456789 };
    64
    65            List<int> digits2 = new List<int> 0 + 112 % 3, MakeInt() };
    66
    67
    68
    69            Console.WriteLine(cards.Count.ToString());
    70
    71            Console.ReadLine();
    72
    73        }

    74
    75               static int MakeInt()
    76
    77        {
    78
    79            return DateTime.Now.Second;
    80
    81        }

    82
    83
    84
    85    }

    86
    87


    匿名类型

    Anonymous Types

    匿名类型把一系列的只读属性封装在一个对象里面,而并没有指定这个对象的类型编译器会给这个对象类型一个名字,但是这个名字在代码级别是不可用的。

    看下面的代码:

    // anon 被编译成匿名类型 注意下面的Name Age都没有定义

                var anon = new { Name = "Terry", Age = 34 };

                //anon.Age = 23;

      最后一行如果执行编译器会给出一个错误:         

            Property or indexer 'AnonymousType#1.Age' cannot be assigned to -- it is read only        

    这个错误信息可以印证上面的说法。继续扩展上面的代码,一起看:

     

    var card = from c3 in cards

                           
    where c3.ID > 13

                           
    //select c3;

                           select 
    new {MyID= c3.ID,MyPassword= c3.Password };

                
    foreach (var item in card )

                
    {

                  Console.WriteLine(item.MyID.ToString()
    +"---"+item.MyPassword );

                    

                }


    这里的代码是匿名类型的一个典型应用,详细请查阅:

    <ms-help://MS.MSDNQTR.v90.en/dv_csref/html/59c9d7a4-3b0e-475e-b620-0ab86c088e9b.htm>

     

    扩展方法

    Extension Methods

    扩展方法是一个静态方法,可以关联在一种类型上,所以这个方法可以在他处调用。这样仿佛给某一个类型添加了方法!而实际上我们并没有改变原有的代码。

    详情参阅:ms-help://MS.MSDNQTR.v90.en/dv_csref/html/175ce3ff-9bbf-4e64-8421-faeb81a0bb51.htm

      public static class Test

        {

            public static string RemoveWhiteSpace(this string s)

            {

                return s.Replace(" ", "");

            }

        }

    定义了上面的方法之后,我们可以在string类型上使用这个方法下面是在开发环境中的截图:

    4 

    匿名方法

    Anonymous Functions

     

     

     

     1delegate void Testdelegate(string s);
     2
     3        static void Show(string s)
     4
     5        {
     6
     7            Console.WriteLine(s);
     8
     9        }

    10
    11 //1.1里面我们这样做
    12
    13            Testdelegate t = new Testdelegate(Show);
    14
    15
    16
    17            //2.0
    18
    19            Testdelegate t2 = delegate(string str) { Console.WriteLine(str); };
    20
    21
    22
    23            //3.0
    24
    25            Testdelegate t3 = (X) => { Console.WriteLine(X); };
    26
    27            t("Kingtest1");
    28
    29            t2("20022396");
    30
    31            t3("20022458"); 
    32

    3.0里面我们的代码使用了Lambda表达式,详情参阅:

    ms-help://MS.MSDNQTR.v90.en/dv_csref/html/57e3ba27-9a82-4067-aca7-5ca446b7bf93.htm

    Lambada表达式在C#3.0中的典型应用:

    ms-help://MS.MSDNQTR.v90.en/fxref_system.core/html/5a7a3466-7d99-dea6-a9fa-04043f951573.htm
     

                        

    // Split the string into individual words.

                
    string[] words = sentence.Split(' ');



                
    // Prepend each word to the beginning of the 

                
    // new sentence to reverse the word order.

                
    string reversed = words.Aggregate((workingSentence, next) =>

                                                      next 
    + " " + workingSentence);

                
    bool b = words.All(w => w.Length > 5);

                
    bool b2 = words.Any(w => w.Length > 10);

    一句话体会:

        c#新特性的应用需要我们深入的学习以及在团队范围内的知识共享,这样才不致于让代码显得怪异

    From:http://www.cnblogs.com/me-sa/archive/2008/04/23/1166688.html