zoukankan      html  css  js  c++  java
  • NET基础课--Linq第一讲

    在说LINQ之前必须先说说几个重要的C#语言特性

    一:与LINQ有关的语言特性

      1.隐式类型

        (1)源起

          在隐式类型出现之前,在声明一个变量的时候,

          总是要为一个变量指定他的类型甚至在foreach一个集合的时候,

          也要为遍历的集合的元素,指定变量的类型隐式类型的出现,程序员就不用再做这个工作了。

        (2)使用方法   

          var a = 1; //int a = 1;
          var b = "123";//string b = "123"; 
          var myObj = new MyObj();//MyObj myObj = new MyObj()

          上面的每行代码,与每行代码后面的注释,起到的作用是完全一样的

        (3)你担心这样写会降低性能吗?

          我可以负责任的告诉你,这样写不会影响性能!

          上面的代码和注释里的代码,编译后产生的IL代码(中间语言代码)是完全一样的

          (编译器根据变量的值,推导出变量的类型,才产生的IL代码)      

        (4)这个关键字的好处:

          你不用在声明一个变量并给这个变量赋值的时候,写两次变量类型

          在foreach一个集合的时候,可以使用var关键字来代替书写循环变量的类型

         (5)注意事项

          你不能用var关键字声明一个变量而不给它赋值,因为编译器无法推导出你这个变量是什么类型的。

                     var只能用于局部变量。建议:只在写linq查询表达式和创建匿名类时使用。

      2.匿名类

        (1)源起

          创建一个对象,一定要先定义这个对象的类型吗?不一定的!

        (2)使用 

             var obj = new {Guid.Empty, myTitle = "匿名类型", myOtherParam = new int[] { 1, 2, 3, 4 } };
             Console.WriteLine(obj.Empty);//另一个对象的属性名字,被原封不动的拷贝到匿名对象中来了。
             Console.WriteLine(obj.myTitle);
             Console.ReadKey();

          new关键字之后就直接为对象定义了属性,并且为这些属性赋值且对象创建出来之后,在创建对象的方法中,还可以畅通无阻的访问对象的属性

          当把一个对象的属性拷贝到匿名对象中时,可以不用显示的指定属性的名字,这时原始属性的名字会被“拷贝”到匿名对象中

        (3)注意    

          如果你监视变量obj,你会发现,obj的类型是Anonymous Type类型的

          不要试图在创建匿名对象的方法外面去访问对象的属性!

        (4)优点

          这个特性在网站开发中,序列化和反序列化JSON对象时很有用

      3.自动属性

        (1)源起

          为一个类型定义属性,我们一般都写如下的代码:    

             public class MyObj2
            {
                private Guid _id;
                public Guid id 
                {
                    get { return _id; }
                    set { _id = value; } 
                }
            }

          但很多时候,这些私有变量对我们一点用处也没有,比如对象关系映射中的实体类。

          自C#3.0引入了自动实现的属性,以上代码可以写成如下形式:

            public class MyObj
            {
                public Guid id { get; set; }
                public string Title { get; set; }
            }

          这个特性也和var关键字一样,是编译器帮我们做了工作,不会影响性能的

      4.初始化器

        (1)源起我们创建一个对象并给对象的属性赋值,代码一般写成下面的样子    

                var myObj = new MyObj();
                myObj.id = Guid.NewGuid();
                myObj.Title = "allen";

          自C#3.0引入了对象初始化器,代码可以写成如下的样子  

          var myObj1 = new MyObj() { id = Guid.NewGuid(), Title = "allen" };

          如果一个对象是有参数的构造函数那么代码看起来就像这样

          var myObj1 = new MyObj ("allen") { id = Guid.NewGuid(), Title = "allen" };

          集合初始化器的样例代码如下:    

          var arr = new List<int>() { 1, 2, 3, 4, 5, 6 };

      5.委托  可以参考.NET中级课中的关于委托的讲解。

        (1)看一个简单的委托代码    

            delegate Boolean moreOrlessDelgate(int item);
            class Program
            {
                static void Main(string[] args)
                {
                    var arr = new List<int>() { 1, 2, 3, 4, 5, 6,7,8 };
                    var d1 = new moreOrlessDelgate(More);            
                    Print(arr, d1);
                    Console.WriteLine("OK");
    
                    var d2 = new moreOrlessDelgate(Less);
                    Print(arr, d2);
                    Console.WriteLine("OK");
                    Console.ReadKey();
    
                }
                static void Print(List<int> arr,moreOrlessDelgate dl)
                {
                    foreach (var item in arr)
                    {
                        if (dl(item))
                        {
                            Console.WriteLine(item);
                        }
                    }
                }
                static bool More(int item)
                {
                    if (item > 3)
                    { 
                        return true; 
                    }
                    return false;
                }
                static bool Less(int item)
                {
                    if (item < 3)
                    {
                        return true;
                    }
                    return false;
                }
            }

          这段代码中

          <1>首先定义了一个委托类型

            delegate Boolean moreOrlessDelgate(int item);

            你看到了,委托和类是一个级别的,确实是这样:委托是一种类型

            和class标志的类型不一样,这种类型代表某一类方法。

            这一句代码的意思是:moreOrlessDelgate这个类型代表返回值为布尔类型,输入参数为整形的方法

          <2>有类型就会有类型的实例  

            var d1 = new moreOrlessDelgate(More);     
            var d2 = new moreOrlessDelgate(Less);

            这两句就是创建moreOrlessDelgate类型实例的代码,它们的输入参数是两个方法

          <3>有了类型的实例,就会有操作实例的代码   

            Print(arr, d1);
            Print(arr, d2);

            我们把前面两个实例传递给了Print方法

            这个方法的第二个参数就是moreOrlessDelgate类型的

            在Print方法内用如下代码,调用委托类型实例所指向的方法dl(item)

      6.泛型

        (2)使用

          <1>使用简单的泛型。先来看下面的代码:        

                  var intList = new List<int>() { 1,2,3};
                  intList.Add(4);
                  intList.Insert(0, 5);
                  foreach (var item in intList)
                  {
                      Console.WriteLine(item);
                  }
                  Console.ReadKey();

            在上面这段代码中我们声明了一个存储int类型的List容器

            并循环打印出了容器里的值注意:如果这里使用Hashtable、Queue或者Stack等非泛型的容器

            就会导致装箱操作,损耗性能。因为这些容器只能存储Object类型的数据

          <2>泛型类型

            List<T>、Dictionary<TKey, TValue>等泛型类型都是.net类库定义好并提供给我们使用的

            但在实际开发中,我们也经常需要定义自己的泛型类型       

              public static class SomethingFactory<T>
              {
                  public static T InitInstance(T inObj)
                  {
                      if (false)//你的判断条件
                      {
                          //do what you want...
                          return inObj;
                      }
                      return default(T);
                  }
              }

            这段代码的消费者如下:        

                  var a1 = SomethingFactory<int>.InitInstance(12);
                  Console.WriteLine(a1);
                  Console.ReadKey();

            输出的结果为0 。这就是一个自定义的静态泛型类型,

            此类型中的静态方法InitInstance对传入的参数做了一个判断

            如果条件成立,则对传入参数进行操作之后并把它返回,如果条件不成立,则返回一个空值

            注意:[1]传入参数必须为指定的类型,因为我们在使用这个泛型类型的时候,已经规定好它能接收什么类型的参数

                但在设计这个泛型的时候,我们并不知道使用者将传递什么类型的参数进来

                [2]如果你想返回T类型的空值,那么请用default(T)这种形式。因为你不知道T是值类型还是引用类型,所以别擅自用null

          <3>泛型约束

            对于泛型类型的设计者来说,要求使用者传入指定的类型是很有必要的

            因为我们只有知道他传入了什么东西,才方便对这个东西做操作          

        (3)泛型的好处

          算法的重用  类型安全  提升性能

      7.泛型委托!!

        (1)源起

          委托需要定义delgate类型,使用起来颇多不便,而且委托本只代表某一类方法,我们需要更通用的。

          开发人员经常使用的委托基本可以归为三类

          <1>Predicate泛型委托 来看看他的定义:    

              // 摘要:
              //     表示定义一组条件并确定指定对象是否符合这些条件的方法。
              //
              // 参数:
              //   obj:
              //     要按照由此委托表示的方法中定义的条件进行比较的对象。
              //
              // 类型参数:
              //   T:
              //     要比较的对象的类型。
              //
              // 返回结果:
              //     如果 obj 符合由此委托表示的方法中定义的条件,则为 true;否则为 false。
              public delegate bool Predicate<in T>(T obj);

            .net为我们定义了一个委托,委托表示的方法需要传入一个T类型的参数,并且需要返回一个bool类型的返回值。主要用于判断真假。

            除了Predicate泛型委托,.net还为我们定义了更通用的Action和Func两个泛型委托。

          <2>Action泛型委托

            可以有0个到16个输入参数,输入参数的类型是不确定的,但不能有返回值,      

                  var d3 = new Action(noParamNoReturnAction);
                  var d4 = new Action<int, string>(twoParamNoReturnAction);

             注意:尖括号中int和string为方法的输入参数

                static void noParamNoReturnAction()
                {
                    //do what you want
                }
                static void twoParamNoReturnAction(int a, string b)
                {
                    //do what you want
                }

           <3>Func泛型委托

            为了弥补Action泛型委托不能返回值的不足,.net提供了Func泛型委托,

            相同的是它也是最多0到16个输入参数,参数类型由使用者确定,不同的是它规定要有一个返回值,返回值的类型也由使用者确定     

              var d5 = new Func<int, string>(oneParamOneReturnFunc);

            注意:string类型(最后一个泛型类型)是方法的返回值类型

                static string oneParamOneReturnFunc(int a)
                {
                    //do what you want
                    return string.Empty;
                }

      8.匿名方法

        语法:delegate关键字声明,后面跟随方法的参数列表,然后在编写方法体。

                btn.click += delegate(object sender,eventargs e)
                {
    messagebox.show("hello word");
    };

              发现没有,参数未使用,那怎么办?省略呗。

                btn.click += delegate
                {
                     messagebox.show("hello word");
                };
    

       还有一种使用方法,就是在匿名方法内部使用方法体外部的变量。

              string s="hello word";
              btn.click += delegate
                {
                     messagebox.show(s);
                };

      9.Lambda表达式

               匿名方法的等价物,目的:进一步简化了匿名方法的写法 

               btn.click += (x,y) => { messagebox.show("hello,word");messagebox.show((x as button).text)}
    

           =>是lambda操作符,右边为方法体,左边(x,y)则代表delegate(object x,eventargs y) 即参数

               再看下面的例子:

                List<int> arr = new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
                arr.ForEach(new Action<int>(delegate(int a) { Console.WriteLine(a); }));
                arr.ForEach(new Action<int>(a => Console.WriteLine(a)));

           匿名方法的代码:delegate(int a) { Console.WriteLine(a); }

           使用lambda表达式的代码如下:a => Console.WriteLine(a)

          这里解释一下这个lambda表达式

          <1>

            a是输入参数,编译器可以自动推断出它是什么类型的,

            如果没有输入参数,可以写成这样:() => Console.WriteLine("ddd")

          <3>

            Console.WriteLine(a)是要执行的语句。

            如果是多条语句的话,可以用{}包起来。如果需要返回值的话,可以直接写return语句

      10.扩展方法

        (1)源起 如果想给一个类型增加行为,一定要通过继承的方式实现吗?不一定的!

        (2)使用 来看看这段代码:    

              public static void PrintString(this String val)
              {
                  Console.WriteLine(val);
              }

          消费这段代码的代码如下:    

                var a = "aaa";
                a.PrintString();
                Console.ReadKey();

          我想你看到扩展方法的威力了。本来string类型没有PrintString方法

          但通过我们上面的代码,就给string类型"扩展"了一个PrintString方法

          (1)先决条件

            <1>扩展方法必须在一个非嵌套、非泛型的静态类中定义

            <2>扩展方法必须是一个静态方法

            <3>扩展方法至少要有一个参数

            <4>第一个参数必须附加this关键字作为前缀

            <5>第一个参数不能有其他修饰符(比如ref或者out)

            <6>第一个参数不能是指针类型

          (2)注意事项

            <1>跟前面提到的几个特性一样,扩展方法只会增加编译器的工作,不会影响性能(用继承的方式为一个类型增加特性反而会影响性能)

            <2>如果原来的类中有一个方法,跟你的扩展方法一样(至少用起来是一样),那么你的扩展方法奖不会被调用,编译器也不会提示你

            <3>扩展方法太强大了,会影响架构、模式、可读性等等等等....

     

  • 相关阅读:
    LeetCode 842. Split Array into Fibonacci Sequence
    LeetCode 1087. Brace Expansion
    LeetCode 1219. Path with Maximum Gold
    LeetCode 1079. Letter Tile Possibilities
    LeetCode 1049. Last Stone Weight II
    LeetCode 1046. Last Stone Weight
    LeetCode 1139. Largest 1-Bordered Square
    LeetCode 764. Largest Plus Sign
    LeetCode 1105. Filling Bookcase Shelves
    LeetCode 1027. Longest Arithmetic Sequence
  • 原文地址:https://www.cnblogs.com/tiantianle/p/4906275.html
Copyright © 2011-2022 走看看