C#3.0 新语法学习篇

     C# 现在都4.0了,惭愧 ,现在才开始学3.0的东西. 看来学习这一系列的技术又将是一个漫漫长旅, 希望自己或每一个刚开始学习C#3.0的朋友一起
坚持走下去...
  下面直接从新语法开始慢慢品味吧这些新特性吧... 

 一、隐式类型化的变量
   (1) 、隐式类型化的局部变量
  例如:
    static void Main()
    {
      var myInt=0;
      var myBool=true;  //可以根据初始值自动推断出变量的数据类型  注意关键字 var的使用.
    }
    可以通过反射来验证:
     myInt.GetType().Name;    --输出 Int32
     myBool.GetType().Name;   --输出 Boolean

    还可以对基类库中的所有类型使用隐式类型化 包括数组、泛型、自定义类型
    var evenNumers=new int[]{1,2,3,4};
    var myMinivans=new List<MiniVan>();
    var myCar=new SportsCar();
   
    在foreach中使用关键字 var
    foreach(var item in evenNumbers)
    {..}

    隐式类型化变量的限制:
    a. 只能用于方法、属性内部局部变量的声明。
    b. 不能使用var类型 作为方法的返回值类型或参数类型,也不能用它来声明类的数据成员。
    c. 声明的同时必须赋值.  不可以赋值为null,因为编译器不可能推断出该变量在内存中实际指向的数据类型

   注: var myObj=new SportCar();  //编译器已经推断出了它的类型了,所以可以赋值为null
        myObj=null; //这样正确
 
        var  myInt=0; //隐式类型化得局部变量的值可以作为其他任何变量的值
        var  aa=myInt;//可以进行传递
 
(2)、隐式类型化的局部数组
      1.使用新的语法来配置数组类型:
        var a=new[]{1,2,3,4};    a的类型是 int[]
        var b=new[]{1,1.2,2.4};  b的类型是  double[]
        var c=new[]{"we",null,"are",null,"family"};  c的类型是 string[]
        var myCars=new[]{new SportsCar(),new SportsCar()};  myCars的类型是 SportsCars[]
    注意:数组成员必须为同一可推断的类型 如:全部为Int 或 String 或SportsCar类型

  最后要注意的问题:
    1. 如果仅仅为了进行数据类型的声明而使用关键字 var 意义不大,使得我们阅读 困难.其重要应用场景是LINQ技术中.
       通过查询的格式返回动态创建的结果集.
    2.类型一旦推定,就不可以为变量赋予一个不同类型的值:
       如:
           var s="This variable can onlyhold string data!";
            s="This is fine...";
            s=1234;  //错误

 

二、扩展方法
   背景:
      一旦一个类型(类,接口,结构,枚举,委托)被定义然后编译进一个.net程序集后,如果要为该类型添加新的成员或更新、删除
 成员的唯一方法就是修改类型的定义代码.然后重新编译并更新新的程序集(或者尝试更高级的方法,如:使用命名空间 System.Reflection.Emit
 下的类对已编译的类型做运行时的动态改造.
  
  初始印象:
    C#3.0的扩展方法,允许现存已编译的类型(如:.net类库中的类)和当前即将被编译的类型(扩展方法的类型)在不需要直接重新编译的情况下获得功能上的扩展.

  目标:
   当需要使类型支持一系列的成员(为了实现多态)但不能改变原始定义时,扩展方法可以帮我们解决问题.
   使用扩展方法,你可以为已编译的类型添加功能,同时这些方法与原始定义分开存放.

  限制:
   (1),定义扩展方法的类必须为静态类,因此扩展方法也必须为静态方法
   (2)、所有扩展方法都需要使用this关键字对第一个参数(仅对第一个参数) 进行修饰.
   (3)、扩展方法只可以被内存中正确的实例调用,或者通过其所处的静态类被调用.

  下面 我们以扩展.NET类库中已有的类型添加新的功能为例,开始扩展方法的学习
   // 编写一个我们自己的扩展工具类 MyExtensions
   namespace myTest
   {
    static class MyExtensions
     {
        //扩展所有对象都能够显示自身的类型名称和所处程序的程序集能力
        public static void DisplayDefiningAssembly(this object obj)
        {
          Console.WriteLine("{0} lives here:\n\t->{1}\n",obj.GetType().Name,
          Assembly.GetAssembly(obj.GetType()));
        }
        //扩展所有Int类型都支持反转自身的能力 如:123 将返回 321
        public static int ReverseDigits(this int i)
        {
           char[] c=i.ToString().ToCharArray();
           Array.Reverse(c);
           string newC=new string(c);
           return int.Parse(newC);
        }
   
     }
     static class TestrUtilClass
     {
        //为Int类型添加一个指定格式的报告自己的扩展方法
        public static void Foo(this int i)
        {
           Console.writeline("{0} called the Foo() method.",i);
        }
        //重载  接受多个参数
        public static void Foo(this int i,string msg)
        {
           Console.WriteLine("{0} called Foo() and told me: {1}",i,msg);
        }
     }
   }

   1.在实例层次上调用扩展方法
     using MyTest;
     static void Main()
     {
        int myInt=12345678; 
        int.DisplayDefiningAssembly();  //实例调用  所有对象都拥有DisplayDefiningAssembly()方法
       
        DataSet d=new DataSet();
        d.DisplayDefiningAssembly();    //实例调用  所有对象都拥有DisplayDefiningAssembly()方法

        myInt.Foo();                                          //实例调用  仅Int类型拥有此Foo()方法
        myInt.Foo("Intsthat Foo?Who would have thought it!"); //实例调用  仅Int类型拥有此Foo()方法,this声明的参数忽略

        bool flag=false;
        flag.Foo();//编译错误   //bool类型不拥有此扩展方法.
     }
   2.静态调用扩展方法
      using MyTest;
     static void Main()
     {
        int myInt=12345678;
        MyExtensions.ReverseDigits(myInt); //静态调用
        TestrUtilClass.Foo(myInt);   //静态调用
        TestrUtilClass.Foo(myInt,"Ints that Foo? Who would have thought it!"); //静态调用

        DataSet d=new DataSet(); //静态调用
        MyExtensions.DisplayDefiningAssembly(d);//静态调用 
     }
  值得注意的一点是: 在使用扩展方法的地方 必须导入这个扩展类型的命名空间

三、对象初始化器:
     一个对象初始化器仅仅是一个有若干特定值组成的逗号分隔,并且首尾分别使用{}围起来。
    初始化对象的几种方式:
     1). 使用特性  通过命名属性语法间接为特性赋值
      [AttributeUsage(AttributeTargets.Class,Inherited=false,AllowMultiple=false]
      public sealed classSomeInterestingAttribute:Attribute
      {..};
     2). C#3.0 以前的语法
       Point first=new Point(); //通过属性初始化对象
       first.X=100;
       first.Y=200;

       Point aa=new Point(20,20);//通过构造函数
     3). 新的语法
       var aa=new Point {X=30,Y=50};
       Point bb=new Point{X=20,Y=30};

   A、使用初始化语法调用自定义构造函数
      Point finalPoint=new Point {X=30,y=30}; //隐式调用 默认构造函数
       Point finalPoint=new Point(){X=30,y=30};//显式调用默认构造函数 //这种写法没有意义不大
      Point finalPoint=new Point(PointColor.Gold){X=100,y=300}; //显式调用一个参数的自定义构造函数 并初始化其他成员

   B、初始化内部类型
     Rectangle myRect=new Rectangle
     {
        TopLeft=new Point{X=100,Y=100},
        BottomRight=new Point{X=200,Y=200}
     }
   C、初始化集合类型
     1)、初始化标准数组
         int [] arr={1,2,3,4,5};
     2)、初始化List<int>泛型
         List<int> myGenericList=new List<int> {1,2,3,4,5};   //必须是实现了IConnections<T>接口的集合类型才可以使用这种语法. 如:ArrayList不能使用
     3)、初始化复杂类型的容器
         List<Point> myPoints=new List<Point>
         {
            new Point{X=2,Y=3},
            new Point{X=3,Y=4},
            new Point{X=4,Y=5}
         };
         foreach(var pt in myPoints)
         {
            Console.WriteLine(pt);
         }

        或 更复杂的
         List<Rectangle> myPoints=new List<Rectangle>
         {
            new Rectangle {TopLeft=new Point{X=10,Y=10},BottomRight=new Point{X=20,Y=30}},
             new Rectangle {TopLeft=new Point{X=20,Y=40},BottomRight=new Point{X=30,Y=30}},
         };
         foreach(var pt in myPoints)
         {
            Console.WriteLine(pt);
         }

 四、 匿名类型
       如果定义的类只是用于当前的应用程序,而不需要在项目间重用,需要用到匿名类型.

       其实匿名类型也就是隐式类型局部变量(Var)关键子和对象初始化语法的一个应用.
     
      下面看具体的例子:
        //构建一个匿名对象来表示一个汽车
        var myCar=new {Color="Red",Make="BaoMa",CurrentSpeed=50};
        //myCar是隐式类型化得(必须这么做),并没有使用强类型化的类定义来构建汽车类型

        //使用属性语法获取 和设置对象的每一个变量
         myCar.Color="Black";   //赋值
         Console.WriteLine("My car is the color {0}.",myCar.Color); //取值


       匿名类型的一些细节:
        匿名类型都继承与Object,因此我们可以调用 ToString(),GetHashCode(),Equals()或者GetType()方法.
        还可以通过反射来验证它们.
      
      需要注意的是:匿名类型的相等语义
       如果两个结构和都一样的匿名类型
       Equals() 返回 True   比较的是内容是否相等
       ==       返回 False  因为匿名类型没有重载C#相等运算符。 所以使用==时 比较的是两者的引用

       匿名类型还可以包含匿名类型
      var  purchaseItem=new {TimeBought=DateTime.Now,ItemBought=new {Color="red",make="Saab"}};
     
      小结:
          匿名类型应当谨慎使用,尤其在使用LINQ技术时,匿名类型本身的一些限制:
          1).匿名类型继承Object
          2)、匿名类型不支持事件,自定义方法和自定义重写。
          3)、匿名类型是隐式封闭的(sealed)。
          4)、匿名类型的创建只使用默认构造函数

五、Lambda表达式
     是C#2.0中的匿名方法一个简写方式,使的我们的代码更加简洁.
    
    说起Lambda表达式 还得从委托谈起

   (1)、首先看传统的委托使用
     //定义在System命名空间下的 public delegate bool Predicate<T>(T obj) 泛型委托 用于包装任何返回布尔类型的对象
     class Program
     {
        static void Main(string[] args)
        {
            var list=new List<int>(){2,4,1,5}; //使用C#3.0 的集合初始化语法创建一个整形列表

            Predicate<int> callback=new Predicate<int>(CallMeHere);//注册委托,使其指向代理方法

            List<int> evenNumbers = list.FindAll(callback); //通过Predicate委托包装所有的项  FindAll()方法只把返回为true的项添加到List<int>

            foreach (var item in evenNumbers)
            {
                Console.WriteLine(item);
            }
            Console.ReadLine();
        }
        //自定义检索条件
        static bool CallMeHere(int i)
        {
            return (i % 2) == 0;
        }
    }

  (2)、使用匿名方法
       static void Main(string[] args)
        {
            var list=new List<int>(){2,4,1,5}; //使用C#3.0 的集合初始化语法创建一个整形列表

            List<int> evenNumbers = list.FindAll(delegate(int i) {return (i%2)==0;}); //使用匿名方法 

            foreach (var item in evenNumbers)
            {
                Console.WriteLine(item);
            }
            Console.ReadLine();
        }
     注意: 使用匿名方法的好处是不用创建委托实例,以及编写一个独立的方法.

  (3)、使用Lambda表达式
      static void Main(string[] args)
        {
            var list=new List<int>(){2,4,1,5}; //使用C#3.0 的集合初始化语法创建一个整形列表

            List<int> evenNumbers = list.FindAll(i=>(i%2)==0); //使用Lambda表达式
            //也可以这样 var evenNumbers=list.FindAll(i=>(i%2)==0); 使用隐式类型化

            foreach (var item in evenNumbers)
            {
                Console.WriteLine(item);
            }
            Console.ReadLine();
        }
     好处:语言更加简洁,而且而且传统的委托语法消失的无影无踪.
     

    深入分析Lambda表达式
     Lambda表达式由三部分组成
      如:i=>(i%2)==0;
      参数列表  i就是参数列表
      =>标记 
      表达式  (i%2)==0就是表达式
    
     Lambda表达式的参数既可以是显示类型化的 也可以使隐式类型化的。
      var evenNumbers=list.FindAll(i=>(i%2)==0);  //现在参数是隐式类型化的  表示参数i的数据类型是整形,由编译器推断出来
     也可以显示声明
     var evenNumbers=list.FindAll((int i)=>(i%2)==0);  //显示类型化

     单个隐式类型化参数的参数列表的括号可以被省略,也可以使用括号
     var evenNumbers=list.FindAll((i)=>(i%2)==0);
     表达式也可以使用括号包围
     var evenNumbers=list.FindAll((i)=>((i%2)==0));

    上面的Lambda表达式只关联一条语句,下面请看 用多行语句来定义Lambda表达式
     var evenNumbers=list.FindAll(i=>
       {
          Console.WriteLine("Called by FindAll()!");  //关联多条语句用{}即可
          bool isEven=((i%2)==0);
          return isEven;
       }
       );
   
   含有多个或零个参数的Lambda表达式
    如:
      实例化一个委托,用Lambda表达式  注意有多个参数的Lambda表达式
      m.SetMathHandler((msg,result)=>{Console.WriteLine("Message:{0},Result:{1}",msg,result);});
     
     也可以如下:
      m.SetMathHandler((string msg,int result)=>{Console.WriteLine("Message:{0},Result:{1}",msg,result);});

    如果使用Lambda初始化一个没有参数的委托,可以使用空括号表示表达式的参数列表
      ViewSimpleDelegate d=new ViewSimpleDelegate( ()=> {return "Hello Word";});
      d.Invoke();

六、自动实现属性
   当属性访问器中不需要其他逻辑时,自动实现的属性可使属性声明变得更加简洁。
   如:
   class Test
   {
     public double Price{ get; set; }
     public string Name { get; private set; } // read-only  只读属性
     public int CustID{ get; private set; }
   }
 注意:自动实现的属性必须同时声明 get 和 set 访问器。若要创建 readonly 自动实现属性,请给予它 private set 访问器。


七、分部方法  再次不做讲述 详查MSDN.

 


 

你可能感兴趣的:(C#)