中匿名函数,中的发展

By admin in 4858.com on 2019年4月17日

原先一贯迷迷糊糊的,现在到底搞领会。

一、匿名类:[ C# 叁.0/.NET 叁.x 新扩充特色 ]

一、简介

  委托是壹体系型,由重点字delegate申明。确切的说,委托是1种可用于封装命名恐怕匿超格局的引用类型。 
它相仿于 C++
中的函数指针,而且是项目安全和有限支撑的。

  委托项指标宣示与艺术具名相似,有五个返回值和Infiniti制数目任意档次的参数。必须使用具备十分再次来到类型和输入参数的情势或
lambda 表明式实例化委托。

  委托允许将艺术作为参数进行传递。
  委托可用来定义回调方法。
  委托能够链接在1块儿;例如,能够对八个风云调用多少个法子。
  方法不必与寄托具名完全合作。

正文内容

  • 信托和泛型委托
    • 委托发展:C# 中央委员托的腾飞
    • 泛型委托
  • 委托
    • 声明(定义)委托
    • 实例化委托
    • 调用委托
    • 中匿名函数,中的发展。用 Lambda表明式创立和实例化委托
  • .NET 提供的委托
    • Action 委托
    • Func 委托
    • Predicate 委托
  • 参考资料
  • 修改记录

Lambda表达式

一.一 不佳意思,小编匿了

   在支付中,大家偶尔会像上面包车型地铁代码同样声美素佳儿(Friso)个匿名类:能够看到,在匿名类的语法中并从未为其命名,而是一贯的二个new
{
}
就完事了。从表面看来,大家根本不可能知道这些类是干神马的,也不晓得它有啥意义。

4858.com 1)

                    var annoyCla1 = new


    {

        ID = 10010,

        Name = "EdisonChou",

        Age = 25


    };

 

    Console.WriteLine("ID:{0}-Name:{1}-Age:{2}", annoyCla1.ID,annoyCla1.Name, annoyCla1.Age);

4858.com 2)

  经过调节和测试运转,我们发现匿名类完全能够兑现签名类的坚守:

4858.com 3

二、Delegate

   Delegate至少0个参数,至多31个参数,能够无重返值,也能够钦定重回值类型。

     public delegate int MethodDelegate(int x, int y);
        private static MethodDelegate method;
        static void Main(string[] args)
        {
            method = new MethodDelegate(Add);
            Console.WriteLine(method(10,20));
            Console.ReadKey();
        }

        private static int Add(int x, int y)
        {
            return x + y;
        }

 

下载 Deom

Lamda表明式基本写法是()=>{ };
拉姆da和措施1致都得以流传参数和具有再次回到值。(int x)=>{return x;};
Lambda表明式并不是C#独有的,其余语言也有着Lambda表达式。

1.贰 深刻匿名类背后

   既然我们发现匿名类能够完全落实签字类的魔法,那么大家可以大胆估摸编写翻译器鲜明在其间帮大家转变了1个类似签字类的class,于是,我们还是凭借反编写翻译工具对其进展研讨。通过Reflector反编写翻译,我们找到了编写翻译器生成的匿名类如下图所示:

4858.com 4

  从上海图书馆能够看到:

  (1)匿名类被编写翻译后会生成二个[泛型类],能够观察上海图书馆中的<>f__AnonymousType0<<ID>j__TPar,
<Name>j__TPar, <Age>j__TPar>便是几个泛型类;

  (贰)匿名类所生成的品质都是只读的,能够观望与其对应的字段也是只读的;

  4858.com 5

  所以,假设大家在先后中为属性赋值,那么会产出错误;

  4858.com 6

  (③)能够见到,匿名类还重写了基类的四个点子:Equals,GetHashCode和ToString;咱们得以看看它为大家所生成的ToString方法是怎么来完成的:

  4858.com 7

  完毕的功用如下图所示:

4858.com 8

三、Func<T>

 在行使 Func<T, TResult> 委托时,不必显式定义3个打包只有3个参数的方法的寄托。

 以下示例简化了此代码,它所用的诀要是实例化 Func<T, TResult> 委托,而不是显式定义2个新委托并将命超级模特式分配给该信托。

public class GenericFunc
{
   public static void Main()
   {
      // 依旧是用命名方法实例化委托类型
      Func<string, string> convertMethod = UppercaseString;
      string name = "Dakota";
      // 依旧是通过委托实例调用该方法
      Console.WriteLine(convertMethod(name));
   }

   private static string UppercaseString(string inputString)
   {
      return inputString.ToUpper();
   }
}

下边包车型大巴示范演示怎么样评释和应用 Func<T, TResult> 委托。  

此示例声惠氏(Karicare)个 Func<T, TResult> 变量,并为其分配了多少个将字符串中的字符转变为题写的
lambda 表明式。  

继之将封装此方法的信托传递给Enumerable.Select 方法,以将字符串数组中的字符串改动为大写。

static class Func
{
   static void Main(string[] args)
   {
      // 声明了一个Func委托类型的变量selector并用Lambda表达式进行实例化 
      // 这个Lambda表达式将用来获取一个字符串并将这个字符串转化为大写并返回
      Func<string, string> selector = str => str.ToUpper();

      // 创建一个字符串数组
      string[] words = { "orange", "apple", "Article", "elephant" };
      // 依次遍历这个字符串数组并调用委托实例selector进行处理
      IEnumerable<String> aWords = words.Select(selector);

      // 输出结果到控制台
      foreach (String word in aWords)
         Console.WriteLine(word);
   }
}      
/*
This code example produces the following output:

   ORANGE
   APPLE
   ARTICLE
   ELEPHANT
*/

 

下载更加多 德姆o

局部文书档案上写Lambda是匿名函数,小编认为是颠三倒四的,Lambda可以代表二个匿名函数,不过还能来表示Linq表明式啊。

1.3 匿名类的共享

  能够设想一下,假使我们的代码中定义了无数匿名类,那么是或不是编写翻译器会为每多少个匿名类都生成3个泛型类呢?答案是不是认的,编写翻译器思索得很远,幸免了再也地生成类型。换句话说,定义了四个匿名类的话要是符合自然标准则能够共享一个泛型类。上面,大家就来看看有哪三种意况:

  (一)如若定义的匿名类与前面定义过的一模同样:质量类型和各种都一致,那么暗中同意共享前二个泛型类

4858.com 9)

                    var annoyCla1 = new


            {

                ID = 10010,

                Name = "EdisonChou",

                Age = 25


            };

 

            Console.WriteLine("ID:{0}-Name:{1}-Age:{2}", annoyCla1.ID,

                annoyCla1.Name, annoyCla1.Age);

            Console.WriteLine(annoyCla1.ToString());

 

                        // 02.属性类型和顺序与annoyCla1一致,那么共同使用一个匿名类


                    var annoyCla2 = new


                {

                    ID = 10086,

                    Name = "WncudChou",

                    Age = 25


                };

            Console.WriteLine("ID:{0}-Name:{1}-Age:{2}", annoyCla1.ID,

                annoyCla1.Name, annoyCla1.Age);

            Console.WriteLine("Is The Same Class of 1 and 2:{0}",

                annoyCla1.GetType() == annoyCla2.GetType());    

4858.com 10)

  通过上述代码中的最终两行:大家能够看清其是不是是贰个类型?答案是:True

4858.com 11

  (2)借使属性名称和种种一致,但质量类型区别,那么依然共同使用1个泛型类,只是泛型参数退换了而已,所以在运行时会生成不同的类:

4858.com 12)

                    var annoyCla3 = new


                {

                    ID = "EdisonChou",

                    Name = 10010,

                    Age = 25


                };

            Console.WriteLine("ID:{0}-Name:{1}-Age:{2}", annoyCla3.ID,

                annoyCla3.Name, annoyCla3.Age);

            Console.WriteLine("Is The Same Class of 2 and 3:{0}",

                annoyCla3.GetType() == annoyCla2.GetType());

4858.com 13)

  大家正好说起固然共享了同2个泛型类,只是泛型参数退换了而已,所以在运行时会生成不同的类。所以,那么能够估算到最终两行代码所彰显的结果应该是False,他们固然都采纳了2个泛型类,可是在运营时生成了八个差异的类。

4858.com 14

  (三)若是数据型名称和类型同样,但逐一差别,那么编写翻译器会另行创制3个匿名类

4858.com 15)

                    var annoyCla4 = new


                {

                    Name = "EdisonChou",

                    ID = 10010,

                    Age = 25


                };

            Console.WriteLine("ID:{0}-Name:{1}-Age:{2}", annoyCla4.ID,

                annoyCla4.Name, annoyCla4.Age);

            Console.WriteLine("Is The Same Class of 2 and 4:{0}",

                annoyCla4.GetType() == annoyCla2.GetType());

4858.com 16)

  运营判别结果为:False

4858.com 17

  通过Reflector,能够发现,编写翻译器确实重新生成了一个泛型类:

4858.com 18

四、Action<T>

Action 委托:未有传来参数,也绝非回去类型,即Void。如:

void Main(string[] args)
        {
            Action say = SayHello;
            say();
        }
        public static void SayHello( )
        {
           Console.WriteLine("Say Hello");
        }

Action<T> 信托:传入参数为T,未有回去类型。如:

void Main(string[] args)
        {
            Action<string> say = SayHello;
            say("Hello");
        }
        public static void SayHello(string word )
        {
            Console.WriteLine(word);
        }

其实ActionFunc的用法大约,差距只是八个有再次回到类型,三个从未有过回去类型,当然Action也能够接匿名方法和Lambda表达式。

匿超级模特式:

void Main(string[] args)
        {
            Action<string> say = delegate(string word)
            {
                Console.WriteLine(word);
            };
            say("Hello Word");
        }

Lambda表达式:

 static void Main(string[] args)
        {
            Action<string> say = s => Console.WriteLine(s);
            say("Hello Word");
        }

 

信托和泛型委托


信托完毕了函数指针,这么些函数指针跟 C
的函数指针差别,它是连串安全的,确认保证被调用的点子具名是毋庸置疑的。只要方法签字跟委托签字相配,给委托的实例能够是实例方法,或是静态方法。

怎么要有那些事物?我们对把数据作为函数参数很熟谙,但有时,有个别方法的操作不是针对性数据,而是本着另1个办法。比如,线程,用线程去实施贰个主意,或是代码段;再例如,事件,事件是委托的特例,等等。

delegate委托

二、匿名情势:[ C# 贰.0/.NET 2.0 新添特色 ]

五、Predicate<T>

 泛型委托:表示定义一组条件并规定钦命对象是还是不是合乎这么些标准的办法。此委托由
Array 和 List 类的二种方式运用,用于在汇集中寻觅成分。

void Main(string[] args)
        {
            Point[] points = { new Point(100, 200), 
            new Point(150, 250), new Point(250, 375), 
            new Point(275, 395), new Point(295, 450) };
            Point first = Array.Find(points, ProductGT10);
            Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
            Console.ReadKey();
        }
        private static bool ProductGT10(Point p)
        {
            if (p.X * p.Y > 100000)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

动用含有 Array.Find 方法的 Predicate 委托寻找 Point 结构的数组。

就算 X 和 Y 字段的乘积大于 拾0,000,此委托代表的方法 ProductGT十 将返回true。

Find 方法为数组的每一个成分调用此委托,在符合测试条件的第二个点处甘休。

信托发展:C# 中央委员托的前进

  • C# 壹.0
    中,通过用在其余地点定义的法子显式开头化委托来创设委托的实例。
  • C# 二.0 引入了匿名格局(anonymous
    method)的定义,用匿名格局开头化委托,在委托中实施未命名的内联语句块。
  • C# 3.0 引进了 Lambda
    表明式,与匿超级模特式的概念类似,但更具表现力并且更简短。匿超情势和
    Lambda 表达式统称为“匿名函数”,类似闭包(Closure)性情。
  • 常备,针对 .NET Framework 三.5 及更加高版本应接纳 Lambda 表明式。

上边的言传身教演示了从 C# 1.0 到 C# 三.0 委托成立进程的提升:

示例1:

4858.com 194858.com 20View Code

namespace MyDelegate
{
    class Program
    {
        delegate void TestDelegate(string s);

        static void M(string s)
        {
            System.Console.WriteLine(s);
        }

        static void Main(string[] args)
        {
            // C# 1.0: 最初的委托语法,用一个方法名初始化委托.
            TestDelegate testdelA = new TestDelegate(M);

            // C# 2.0: 用内联代码初始化委托,这个内联代码成为“匿名方法”.
            // 这个方法把一个字符串作为输入参数.
            TestDelegate testDelB = delegate(string s) { System.Console.WriteLine(s); };

            // C# 3.0: 用 Lambda 表达式初始化委托. 
            // Lambda 表达式也把一个字符串(x)作为输入参数.
            // 编译器可以推断 x 的数据类型.
            TestDelegate testDelC = (x) => { System.Console.WriteLine(x); };

            // 调用委托.
            testdelA("Hello. My name is M and I write lines.");
            testDelB("That's nothing. I'm anonymous and ");
            testDelC("I'm a famous author.");

            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
    }
}

运作结果:

4858.com 214858.com 22View Code

Hello. My name is M and I write lines.
That's nothing. I'm anonymous and
I'm a famous author.
Press any key to exit.

委托基本的使用包蕴扬言一个委托、实例化三个委托、使用三个信托

二.一 从委托的宣示说到

  C#中的匿名形式是在C#2.0引进的,它截至了C#二.0事先版本表明委托的唯1办法是采纳命有名的模特式的1世。可是,那里我们照旧看一下在未有匿名格局在此以前,我们是哪些证明委托的。

  (一)首先定义2个信托项目:

public
                            delegate
                                    void DelegateTest(string testName);

  (二)编写一个适合委托规定的命名情势:

                    public
                            void TestFunc(string name)

        {

            Console.WriteLine("Hello,{0}", name);

        }

  (3)最终声美素佳儿个信托实例:

    DelegateTest dgTest = new DelegateTest(TestFunc);

    dgTest("Edison Chou");

  (肆)调节和测试运行能够得到以下输出:

4858.com 23

  由地方的步凑能够看来,大家要声明一(Wissu)(Karicare)个寄托实例要为其编写多个符合规定的命名方式。可是,即使程序中这一个法子只被这么些委托行使的话,总会以为代码结构有点浪费。于是,微软引进了匿超级模特式,使用匿名格局证明委托,就会使代码结构变得轻易,也会省去实例化的有的费用。

六、总结

    Delegate至少0个参数,至多33个参数,能够无再次回到值,也得以钦赐再次回到值类型

  Func还不错0个至十五个传入参数,必须有所重回值

  Action还不错0个至1四个传入参数,无重回值

  Predicate只好接受一个传播参数,再次回到值为bool类型

 

参照小说:

泛型委托

示例 一 也可改写成泛型方式。如下所示:

示例 2:

4858.com 244858.com 25View Code

namespace MyGenericDelegate
{
    class Program
    {
        delegate void TestGenericDelegate<T>(T s);

        static void GenericM<T>(T s)
        {
            System.Console.WriteLine(s);
        }

        static void Main(string[] args)
        {
            // C# 1.0
            TestGenericDelegate<int> testGenericDelA = new TestGenericDelegate<int>(GenericM);

            // C# 2.0
            TestGenericDelegate<string> testGenericDelB = delegate(string s) { System.Console.WriteLine(s); };

            // C# 3.0
            TestGenericDelegate<double> testGenericDelC = (x) => { System.Console.WriteLine(x); };

            // 调用委托.
            testGenericDelA(123456);
            testGenericDelB("That's nothing. I'm anonymous and ");
            testGenericDelC(123.456);

            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
    }
}

运行结果:

4858.com 264858.com 27View Code

123456
That's nothing. I'm anonymous and
123.456
Press any key to exit.

 

// 声明一个委托,类型是myDelegate,
public delegate void myDelegate(string str); 
public static void HellowChinese(string strChinese) 
{ 
    Console.WriteLine("Good morning," + strChinese); 
    Console.ReadLine(); 
} 
// 实例化   
myDelegate d = new myDelegate(HellowChinese); 
// 使用
d("Mr wang");

二.2 引进匿名方式

  (一)首先,大家来看看上边的例证怎么样行使匿名方式来促成:

DelegateTest dgTest2 = new DelegateTest(delegate(string name)

{

      Console.WriteLine("Good,{0}", name);

});

4858.com 28

从运维结果图中得以看出,原本须要传递格局名的地点大家直接传送了2个方法,那个主意以delegate(参数){方法体}的格式编写,在{}里边直接写了方法体内容。于是,大家不禁满面红光,又有啥不可简化1些工作量咯!

  (二)其次,大家将调换的顺序通过Reflector反编写翻译看看匿名形式是怎么帮大家达成命超格局的效率的。

  一大家能够看看,在编写翻译生成的类中,除了大家和衷共济定义的章程外,还多了五个莫名其妙的分子:

4858.com 29

  2因而逐一查看,原来编写翻译器帮大家调换了二个个体的嘱托对象以及一个民用的静态方法。大家得以大胆估算:原来匿超方式不是不曾名字的主意,依然生成了八个盛名字的秘籍,只不过那一个主意的名字被隐形起来了,而且格局名是编写翻译器生成的。

4858.com 30

4858.com 31

  叁透过地方的剖析,我们照旧不甚领会,到底匿名方式委托对象在先后中是怎么展现的?那里,咱们供给查阅Main方法,但是透过C#代码大家从不发觉有个别得以扶持大家清楚的。那时,大家想要刨根究底就有点麻烦了。万幸,在尧舜指点下,我们知晓能够凭借IL(中间代码)来分析一下。于是,在Reflector中切换体现语言,将C#改为IL,就会合到别的1番天地。

4858.com 32

  (三)由地点的分析,大家能够做出定论:编写翻译器对于匿名格局帮大家做了两件事,壹是生成了三个私有静态的寄托对象和二个个体静态方法;二是将扭转的主意的地址存入了信托,在运作时调用委托对象的Invoke方法实践该信托对象所具备的章程。由此,我们也得以看到,匿名格局供给组合委托行使

委托


以示例 1 为例:

  • 声明(定义)委托

    delegate void TestDelegate(string s);

种种委托描述了点子签字和重回类型等所有细节。如 TestDelegate
定义方法有四个 string 类型的参数 s,并且再次来到 void 类型。

能够在其余地点定义委托,跟定义三个类类似。委托也得以有访问修饰符。

  • 实例化委托

    TestDelegate testdelA = new TestDelegate(M);

声称委托后,必须用某些方法实例化那几个委托。用艺术 M 去实例化委托
testdelA

声称(定义)和实例化委托,有点类似贰个类,类也急需定义,并实例化。

寄托在语法上,总是带有2个参数的构造函数,那一个参数正是委托引用的主意。也正是说,函数指针必须指向二个办法。

  • 调用委托

    testdelA(“Hello. My name is M and I write lines.”);

实例化委托后,通过委托对象的名称(前边是传递给委托的参数)调用委托对象。

委托也得以结合、移除,如下所示:

namespace MyDelegate

{

    delegate void D(int x);

 

    class C

    {

        public static void M1(int i)

        {

            Console.WriteLine("C.M1: " + i);

        }

        public static void M2(int i)

        {

            Console.WriteLine("C.M2: " + i);

        }

        public void M3(int i)

        {

            Console.WriteLine("C.M3: " + i);

        }

    }

}

用如下代码测试:

D cd1 = new D(C.M1);

cd1(-1);                // call M1

D cd2 = new D(C.M2);

cd2(-2);                // call M2

D cd3 = cd1 + cd2;

cd3(10);                // call M1 then M2

cd3 += cd1;

cd3(20);                // call M1, M2, then M1

C c = new C();

D cd4 = new D(c.M3);

cd3 += cd4;

cd3(30);                // call M1, M2, M1, then M3

cd3 -= cd1;             // remove last M1

cd3(40);                // call M1, M2, then M3

cd3 -= cd4;

cd3(50);                // call M1 then M2

cd3 -= cd2;

cd3(60);                // call M1

cd3 -= cd2;                // impossible removal is benign

cd3(60);                // call M1

cd3 -= cd1;                // invocation list is empty so cd3 is null

//        cd3(70);        // System.NullReferenceException thrown

cd3 -= cd1;                // impossible removal is benign
  • 用 Lambda 表达式创立和实例化委托。

    Func myFunc = x => x == 5;

    bool result = myFunc(4); // returns false of course

其中,Func<int, bool> 是.NET
提供的已打包好的信托,用于以参数情势传递的方法,必须再次回到值。那样,就不要显式申明定义委托。该信托输入参数为
int,重返类型为 bool

 

寄托很好用。当编制程序到自然阶段,发现只传递二个或多少个int、strig类型参数是不够的,希望能够把一段代码实行传递来施行有个别操作,委托提供了如此的门径,委托提供了壹种能够直接待上访问类中方法的门路,能够将艺术当作二个参数字传送递从而采纳

二.三 匿有名的模特式增加

  (壹)匿名格局语法糖—特别简化你的代码

  在开拓中,大家往往会采纳语法糖来写匿超方式,例如下边所示:

        DelegateTest dgTest3 = delegate(string name)

        {

           Console.WriteLine("Goodbye,{0}", name);

        };

        dgTest3("Edison Chou");

  能够看到,使用该语法糖,将new
DelegateTest()也去掉了。可见,编写翻译器让我们越来越轻巧了。

  (2)传参也有高校问—向方法中盛传匿超级模特式作为参数

  一在开拓中,大家壹再注明了贰个措施,其参数是三个信托对象,还可以别的符合委托定义的措施。

                    static
                            void InvokeMethod(DelegateTest dg)

    {

         dg("Edison Chou");

    }

  贰我们得以将已经定义的方法地址作为参数字传送入InvokeMethod方法,例如:InvokeMethod(TestFunc);
当然,大家也足以行使匿名方式,不须要独自定义就可以调用InvokeMethod方法。

    InvokeMethod(delegate(string name)

    {

          Console.WriteLine("Fuck,{0}", name);

    });

  (三)省略省略再省略—省略”大括号”

  经过编写翻译器的不停优化,大家发现连delegate后面包车型客车()都得以简轻松单了,大家能够看看下边一段代码:

    InvokeMethod(delegate { 

         Console.WriteLine("I love C sharp!"); 

    });

  而笔者辈之前的概念是如此的:

                    public
                            delegate
                                    void DelegateTest(string testName);

 

                    static
                            void InvokeMethod(DelegateTest dg)

        {

            dg("Edison Chou");

        }

  大家发现定义时办法是亟需传递多个string类型的参数的,可是大家大致了deletegate前面包车型地铁括号之后就从来不参数了,那么结果又是怎么样吗?经过调节和测试,发现结果输出的是:I
love C sharp!

  那时,大家就有点百思不得其解了!明明都未有定义参数,为什么依然满意了适合委托定义的参数条件吧?于是,大家带着题材要么借助Reflector去壹探毕竟。

  壹在Main函数中,能够看来编写翻译器为大家自行抬高了适合DelegateTest这么些委托定义的主意参数,即叁个string类型的字符串。就算,输出的是I
love C
sharp,但它确实是符合章程定义的,因为它会接受2个string类型的参数,固然在方法体中从未选拔到这几个参数。

4858.com 33

  贰正要在Main函数中看看了匿名情势,今后能够看看编写翻译器为大家所生成的命名格局。

4858.com 34

.NET 提供的寄托


匿名函数

3、扩大方法:[ C# 3.0/.NET 叁.x 新扩张特色 ]

Action 委托

该信托以参数情势传递二个举行某操作的办法,不再次来到值。Action
委托有如下多少个重载:

  • Action 委托
  • Action<T> 委托
  • Action<T1, T2> 委托
  • Action<T1, T2, T3> 委托
  • Action<T1, T2, T3, T4> 委托

.NET framework ④.0 提供的重载愈多。可提供 1陆 个输入参数。

以身作则 3:以 Action<T> 带三个参数的委托为例。

4858.com 354858.com 36View Code

using System;

namespace MyAction
{
    class Program
    {
        // 声明委托
        delegate void DisplayMessage(string message);

        static void Main(string[] args)
        {
            // 用 ShowWindowsMessage,采用命名方法实例化 DisplayMessage 委托
            DisplayMessage messageTargetA = new DisplayMessage(ShowWindowsMessage);
            DisplayMessage messageTargetB = ShowWindowsMessage;
            // 用 ShowWindowsMessage,采用命名方法实例化 Action 委托
            Action<string> messageTargetC = ShowWindowsMessage;
            // 用 ShowWindowsMessage,采用匿名方法实例化 Acton 委托
            Action<string> messageTargetD = delegate(string s) { ShowWindowsMessage(s); };
            // 用 ShowWindowsMessage,采用 Lambda 表达式实例化 Acton 委托
            Action<string> messageTargetE = s => ShowWindowsMessage(s);

            messageTargetA("Hello, World!");
            messageTargetB("Hello, World!");
            messageTargetC("Hello, World!");
            messageTargetD("Hello, World!");
            messageTargetE("Hello, World!");
            System.Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
        private static void ShowWindowsMessage(string message)
        {
            System.Console.WriteLine(message);
        }
    }
}

运行结果:

4858.com 374858.com 38View Code

Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Press any key to exit.

该示例最简单易行的花样也可写成:

4858.com 394858.com 40View Code

Action<string> messageTarget = s => System.Console.WriteLine(s);
messageTarget("Hello, World!");
System.Console.WriteLine("Press any key to exit.");
Console.ReadKey();

地点已经讲到匿名函数了。函数和措施等价,用来封装壹段代码片以便随时使用,有的有重回值有的无重返值。写在类里面。

3.一 神奇—初玩扩大方法

  (壹)提到扩大方法,小编想大多数的园友都不目生了。可是依然来看望MSDN的概念:

MSDN
说:
扩大方法使您能够向现存项目“添加”方法,而无需创制新的派生类型、重新编写翻译或以其余措施修改原始类型。那里的”加多”之所以选用引号,是因为并从未真的地向内定项目丰硕方法。

  那么,有时候我们会问:缘何要有扩张方法吗?此处,大家得以顾名思义地想转手,扩充扩充,那么一定是涉嫌到可扩大性。在空虚工厂情势中,我们得以由此新扩充三个工厂类,而不需求改动源代码就足以切换成新的厂子。那里也是这么,在不修改源码的动静下,为有个别类增加新的主意,也就兑现了类的扩张。

  (2)空说无凭,我们来探望在C#中是怎么来推断增添方法的:通过智能提醒,我们发现成一对主意带了1个针对性下方的箭头,查看”温馨提醒”,大家通晓他是1个扩充方法。所得是乃,原来我们一向对聚集举办筛选的Where()方法还是是扩大方法而不是原生的。

4858.com 41

  大家再来看看使用Where那些扩张方法的代码示例:

4858.com 42)

                    static
                            void UseExtensionMethod()

        {

            List<Person> personList = new List<Person>()

            {

                    new Person(){ID=1,Name="Big Yellow",Age=10},

                    new Person(){ID=2,Name="Little White",Age=15},

                    new Person(){ID=3,Name="Middle Blue",Age=7}

            };

 

                        // 下面就使用了IEnumerable的扩展方法:Where


                    var datas = personList.Where(delegate(Person p)

            {

                    return p.Age >= 10;

            });

 

                    foreach (var data in datas)

            {

                Console.WriteLine("{0}-{1}-{2}", 

                    data.ID, data.Name, data.Age);

            }

        }

4858.com 43)

  上述代码应用了Where增加方法,寻找集合中Age>=十的多寡产生新的数据集并出口:

4858.com 44

  (叁)既然扩大方法是为了对类进行扩充,那么大家好还是不佳打开自定义扩张呢?答案是必须能够。我们先来看望扩张方法是哪些的定义的,能够透过刚刚的IEnumerable接口中的Where方法定义来探望有啥规则:通过
转到定义
的秘技,大家可以观望在System.Linq命名空间下,有号称Enumerable的如此2个静态类,它的积极分子方法全是静态方法,而且每一个方法的绝大繁多第三参数都是以this开始。于是,大家能够计算出,扩充方法的多个要素是:静态类静态方法以及this关键字

                    public
                            static
                                    class Enumerable

    {

                    public
                            static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer);

    }

  那么难题又来了:为什么一定得是static静态的吗?这几个我们都掌握静态方法是不属于有些类的实例的,约等于说我们不供给实例化那些类,就可以访问这么些静态方法。所以,你懂的啦。

  (4)看完扩张方法叁要素,大家就来机关入手写贰个扩展方法:

4858.com 45)

                    public
                            static
                                    class PersonExtension

    {

                    public
                            static
                                    string FormatOutput(this Person p)

        {

                    return
                            string.Format("ID:{0},Name:{1},Age:{2}",

                p.ID, p.Name, p.Age);

        }

    }

4858.com 46)

  上边那一个扩充方法成功了叁个格式化输出Person对象属性消息的字符串构造,能够做到地点例子中的输出效果。于是,大家能够将上面包车型地铁代码改为以下的措施打开输出:

4858.com 47)

                    static
                            void UseMyExtensionMethod()

        {

            List<Person> personList = new List<Person>()

            {

                    new Person(){ID=1,Name="Big Yellow",Age=10},

                    new Person(){ID=2,Name="Little White",Age=15},

                    new Person(){ID=3,Name="Middle Blue",Age=7}

            };

 

                    var datas = personList.Where(delegate(Person p)

            {

                    return p.Age >= 10;

            });

 

                    foreach (var data in datas)

            {

                Console.WriteLine(data.FormatOutput());

            }

        }

4858.com 48)

Func 委托

该信托以参数格局传递的法子,必须重回值。Func 委托有如下多少个重载:

  • Func<TResult> 委托
  • Func<T, TResult> 委托
  • Func<T1, T2, TResult> 委托
  • Func<T1, T2, T3, TResult> 委托
  • Func(<T1, T2, T3, T4, TResult> 委托

.NET framework 四.0 提供的重载更加多。可提供 1陆 个输入参数。

示范 肆:以 Func(T, TResult) 待二个参数的委托为例。

4858.com 494858.com 50View Code

using System;

namespace MyFunc
{
    delegate string ConvertMethod(string inString);

    class Program
    {
        static void Main(string[] args)
        {
            // 用 UppercaseString,以命名方法实例化委托
            ConvertMethod convertMethA = UppercaseString;
            // 用 UppercaseString,以命名方法实例化 Func 委托
            Func<string, string> convertMethB = UppercaseString;
            // 以匿名方法实例化 Func 委托
            Func<string, string> convertMethC = delegate(string s) { return s.ToUpper(); };
            Func<string, string> convertMethD = delegate(string s) { return UppercaseString(s); };
            // 以 Lambda 表达式实例化  Func 委托
            Func<string, string> convertMethE = s => s.ToUpper();

            System.Console.WriteLine(convertMethA("Dakota"));
            System.Console.WriteLine(convertMethB("Dakota"));
            System.Console.WriteLine(convertMethC("Dakota"));
            System.Console.WriteLine(convertMethD("Dakota"));
            System.Console.WriteLine(convertMethE("Dakota"));

            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
        private static string UppercaseString(string inputString)
        {
            return inputString.ToUpper();
        }
    }
}

运行结果:

4858.com 514858.com 52View Code

DAKOTA
DAKOTA
DAKOTA
DAKOTA
DAKOTA
Press any key to exit.

该示例最简易的样式也可写成:

4858.com 534858.com 54View Code

Func<string, string> convertMeth = s => s.ToUpper();
System.Console.WriteLine(convertMeth("Dakota"));
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();

唯独壹旦自个儿只想把这段代码片使用2次啊?再声美素佳儿(Friso)个类、三个主意其实太麻烦,这时候匿名函数就颇具了股票总值。用来封装1段代码、直接试行也许传递。

3.二 嗦嘎—探秘扩大方法

  刚刚大家体会了扩展方法的奇妙之处,今后大家本着刨根究底的学习态度,借助Reflector看看编写翻译器到底帮大家做了什么职业?

  (壹)通过反编写翻译刚刚那多少个UseMyExtensionMethod方法,大家发现并不曾什么意外之处。

  (贰)那时,我们得以将C#切换来IL代码看看,大概会有另1番获得?于是,果断切换之后,发现了真理!

4858.com 55

  原来编写翻译器在编译时自动将Person.FormatOutput改变为了PersonExtension.FormatOutput,那时大家好像一语成谶,所谓的恢弘方法,原来正是静态方法的调用而已,所德是乃(原来是那样)!于是,大家能够将这么以为:person.FormatOutput()
等同于调用 PersonExtension.FormatOutput(person);

  (三)再查看所编译生成的格局,发现this关键已经破灭了。大家禁不住一声惊讶,原来this只是2个标识而已,标记它是扩展的是哪一个档次,在方法体中得以对这几个类型的实例举行操作。

4858.com 56

Predicate 委托

该信托定义壹组条件并规定钦定对象是还是不是符合这一个原则的法子。此委托由 Array
和 List<T> 类的两种方法应用,用于在联谊中寻觅成分。

以身作则 5:演示在数组中搜索第八个 X*Y>100000 的点。

4858.com 574858.com 58View Code

using System;
using System.Collections.Generic;

namespace MyPredicate
{
    class Program
    {
        static void Main(string[] args)
        {
            Point[] points = { 
                                 new Point(){X = 100,Y = 200}, new Point(){X = 150,Y = 250}, 
                                 new Point(){X = 250,Y = 375}, new Point(){X = 275,Y = 395}, 
                                 new Point(){X = 295,Y = 450}, new Point(){X = 290,Y = 451}
                             };

            Point first = Array.Find(points, ProductGT10);
            Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
        class Point
        {
            public int X { get; set; }
            public int Y { get; set; }
        }
        private static bool ProductGT10(Point p)
        {
            if (p.X * p.Y > 100000)
                return true;
            else
                return false;
        }
    }
}

也得以那几个写:

4858.com 594858.com 60View Code

Point[] points = { 
                     new Point(){X = 100,Y = 200}, new Point(){X = 150,Y = 250}, 
                     new Point(){X = 250,Y = 375}, new Point(){X = 275,Y = 395}, 
                     new Point(){X = 295,Y = 450}, new Point(){X = 290,Y = 451}
                 };

Point first = Array.Find(points,
    (p) =>
    {
        if (p.X * p.Y > 100000)
            return true;
        else
            return false;
    });
Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();

注脚:无需出示创造委托,或是内定泛型方法的参数类型,因为编写翻译器会基于上下文自个儿分明。

 

匿名函数总是和嘱托一齐使用,因为匿名函数方便了寄托的使用(不用声Bellamy(Bellamy)个类了)

3.三 注意—总计扩张方法

  (1)怎么着定义扩充方法:

  定义静态类,并增添public的静态方法,第二个参数
代表 扩大方法的增加类。

  a)
它必须放在2个非嵌套、非泛型的静态类中(的静态方法);

  b)
它起码有1个参数;

  c)
第一个参数必须附加 this 关键字;

  d)
第2个参数不可能有其余别的修饰符(out/ref)

  e)
第1个参数不能够是指针类型

  (二)当大家把扩充方法定义到任何程序集中时,一定要留意调用扩张方法的环境中必要包涵扩张方法所在的*命名空间*!

  (三)假设要增加的类中自然就有和扩大方法的称呼1致的办法,到底会调用成员方法依然增添方法吗?

答案:编写翻译器暗中同意认为二个表明式是要动用三个实例方法,但只要没有找到,就会检讨导入的命名空间和当前命名空间里存有的庞大方法,并合作到符合的方法。

参考资料

 


  • MSDN
    注脚、实例化和平运动用委托.aspx) 
  • MSDN
    提供的信托 
  • 事件(C#
    编制程序指南).aspx)
  • 委托(C#
    编制程序指南).aspx)
  • C# 3.0 Cookbook, Third Edition: More than 250 solutions for C#
    3.0 programmers-Delegates, Events, and Lambda
    Expressions
  • C# 3.0: Master the fundamentals of C# 3.0-Delegates and
    Events
  • 程序设计_猫老鼠主人

 

匿超形式二种达成情势:

 

 

修改记录


  • 2015年1月29日 【UPDATE】

 

下载 Deom

下载越多 Demo

使用Lambda表达式:

1.1 初识Action

MSDN付出的概念:包裹一个方式,该措施不有所参数并且不重回值

  能够利用此委托以参数格局传递方式,而不用显式声明自定义的委托。封装的章程必须与此委托定义的措施签字相对应。相当于说,封装的艺术不得持有参数,并且不得再次来到值。(在
C# 中,该办法必须再次回到 void)平常,那种艺术用于施行某些操作。

  以后,我们来看望哪些运用Action委托:

  (1)先看看在此以前大家是怎么来利用无再次来到值委托的例证:

4858.com 61 View Code

  能够精通地看出,我们在此以前要先显式注明了二个名字为 ShowValue
的信托,并将对 Name.DisplayToWindow 实例方法的引用分配给其委托实例。

  (2)再看看有了Action委托随后大家怎么来实现位置的效益的事例:

4858.com 62 View Code

  能够精通地看出,现在接纳 Action
委托时,不必显式定义四个卷入无参数进程的委托。

        public delegate void Seep();
        static void Main(string[] args)
        {

            Seep s = () => { Console.WriteLine(2); Console.WriteLine(1); };
        }

1.2 深入Action

  在实际上开销中,我们日常将1个委托实例作为五个艺术的参数举办传递,于是大家来看一下以此典型的处境,再通过Reflector反编写翻译工具查看编写翻译器到底帮大家做了怎样遗闻儿!

  (一)首先来看一下在List集合类型的ForEach方法的概念:

4858.com 63)

                    //


                        // 摘要:

        //     对 System.Collections.Generic.List<T> 的每个元素执行指定操作。


        //


                        // 参数:

        //   action:

        //     要对 System.Collections.Generic.List<T> 的每个元素执行的 System.Action<T> 委托。


        //


                        // 异常:

        //   System.ArgumentNullException:

        //     action 为 null。


                        public
                                void ForEach(Action<T> action);

4858.com 64)

  能够看来,ForEach方法的参数是二个Action委托实例,也正是说是二个无重回值的委托实例。

  (二)定义一个实体类,并透过Action委托行使ForEach方法:

4858.com 65 View Code

  能够观察,大家为ForEach方法传递了一个Action委托的实例,本质上是叁个无重回值的措施指针,遍历输出了种种Person对象的音信。

  4858.com 66

  (三)大概有点童鞋看到上边的依旧有点不解,只要您询问过委托,那么大家能够透过Reflector反编写翻译工具去探访编写翻译器到底做了啥事,Action委托的泰山真面目就会一如掌握:(那里我们能够先看看未有Action的做法,是还是不是索要首先显式注脚了1个无再次回到值的信托,然后是还是不是还要顶贰个命名的无重回值的方法?)

  一将编写翻译好的顺序集拖动到Reflector中,能够观察以下的情景:

4858.com 67

  2现在分别看望编写翻译器为大家自动生成的无再次来到值的寄托定义和办法定义:

4858.com 68

4858.com 69

  能够旁观,不管是自动生成的寄托依然艺术,都是不带重返值的。

  三有了上边包车型地铁剖析,我们再来看看实行的语句是怎么被编写翻译的:

4858.com 70

   能够看到,在编写翻译后的代码里边连new
Action<Person>()都省掉了,大家也能够清楚,在代码中可以进一步简化。可是,首先,大家得询问到底编写翻译器是怎么识别Action委托的。于是,依据前两篇的思路,在反编写翻译后的C#代码看不出什么线索的时候,切换成IL代码1探毕竟:

4858.com 71

  由IL代码能够见到,依旧本来的方法,仍然原来的暗意。委托依然那多少个委托,实行委托依旧施行那多少个格局。那里,大家再来看看List类型的ForEach方法是怎么接纳Action委托的:

4858.com 72

  未来,大家能够清楚,原来所不解的事物以往到底放心了:在ForEach会通过三个循环往复遍历依次调用委托所怀有的法子,那一个法子是一个合乎Action委托定义的无重回值方法。至于,为何咱们能够简单new
Action<T>(),则是编写翻译器为我们提供的2个便宜。例如,大家在运用List<Person>对象的ForEach方法时,大家得以这么写:

personList.ForEach(delegate(Person p)

{

      Console.WriteLine(p.ID + "-" + p.Name + "-" + p.Age);

});

  首先,由于我们是行使的personList这几个目的(List<Person>类型),所以编写翻译器自动识别了泛型委托的T(即钦赐项目)为Person。其次,编写翻译器自动将无再次来到值的匿名方式调换为了new
Action<Person>对象。当然,假诺是有重临值的匿名方式则会调换为钦赐项目标new
Func<T>()对象,这里因为ForEach只接受无参数的寄托实例或方法,所以倘若传入了有再次来到值的匿名格局则会报错。

使用委托

一.3 你毕竟有多少个Action可用?

4858.com 73
  从图中能够见到,.NET
Framework为大家提供了多达拾九个参数的Action委托定义,对于左近的成本处境已经完全够用了。

 

二、有重回类型的嵌入委托—Func

        public delegate void Seep();
        static void Main(string[] args)
        {
            Seep see = delegate () { Console.WriteLine(1); };
        }

2.1 初识Func

MSDN付出的概念:打包一个兼有一个参数并回到 TResult
参数钦命的类型值的格局

  此委托的定义如下:

public
                            delegate TResult Func<in T, out TResult>(T arg)

  (一)in T
:此委托封装的主意的参数类型。

  (二)out
TResult :此委托封装的措施的回来值类型。

  可以动用此委托代表1种能以参数格局传递的法子,而不用显式评释自定义委托。封装的办法必须与此委托定义的点子签名相对应。也正是说,封装的主意必须具有二个透过值传递给它的参数,并且必须重回值。

 

  二.1.一 未有Func时的行使

4858.com 74 View Code

Action、Func、Predicate

  2.一.贰 有了Func后的使用

4858.com 75 View Code

  当然,大家还足以凭借匿名方式越发便利地运用:

4858.com 76 View Code

  能够知晓地看出,今后选择 Func
委托时,不必显式定义2个新委托并将命名格局分配给该信托。

信托须要先声贝因美(Nutrilon)个项目技艺采用,这很麻烦,比如本身在某一个命名空间证明了三个未有再次回到值的委托myDelegate,然后想要那个委托的时候,每种命名空间都要引用那么些命名空间,那太不友善了。

2.2 深入Func

微软直接就在框架中概念了两种委托:Action、Func、Predicate。那二种委托是C#的System命名空间中早就定义好的,如Action

  2.2.一 用法先行:爽一下

  大家曾经精通Func委托是带内定重返值类型的信托,那么大家来看看在事实上付出情况的一幕。依然以刚刚那一个数据集合PersonList为例,在重重时候我们需求对从数据库中读取的数额集合进行一回筛选,那时大家得以采纳List集合的Select方法,我们将贰个Func委托实例作为艺术参数字传送递给Select方法,就足以回来1个顺应咱们钦点条件的新数据集合。

  (一)先来探视Select方法的定义:

4858.com 77)

                    //


                        // 摘要:

        //     将序列中的每个元素投影到新表中。


        //


                        // 参数:

        //   source:

        //     一个值序列,要对该序列调用转换函数。


        //


                    //   selector:

        //     应用于每个元素的转换函数。


        //


                        // 类型参数:

        //   TSource:

        //     source 中的元素的类型。


        //


                    //   TResult:

        //     selector 返回的值的类型。


        //


                        // 返回结果:

        //     一个 System.Collections.Generic.IEnumerable<T>,其元素为对 source 的每个元素调用转换函数的结果。


        //


                        // 异常:

        //   System.ArgumentNullException:

        //     source 或 selector 为 null。


                        public
                                static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);

4858.com 78)

  可以阅览,Select方法中的参数接纳了Func泛型委托,依据泛型委托的定义TSource和TResult分别表示要传播的数据类型以及要赶回的数据类型。

  (二)再来看看怎么着在程序中动用Func委托:

  首先定义1个与源数据类型不一致的新数据类型作为再次来到值类型:

                    public
                            class LitePerson

    {

                    public
                            string Name { get; set; }

    }

  壹规范定义版:

4858.com 79)

            List<Person> personList = GetPersonList();

 

            IEnumerable<LitePerson> litePersonList = personList.Select<Person, LitePerson>(

                    new Func<Person, LitePerson>

                (

                    delegate(Person p)

                    {

                    return
                            new LitePerson() { Name = p.Name };

                    }

                )

            );    

4858.com 80)

  贰呀哈简化版:借助理编辑译器提供的自动识别,简化大家的代码

            IEnumerable<LitePerson> litePersonList = personList.Select(

                    delegate(Person p)

                {

                    return
                            new LitePerson() { Name = p.Name };

                }

            );

  叁绝逼懒人版:借助匿名类和泛型能够大大简化我们的代码

                    var liteList = personList.Select(delegate(Person p)

            {

                    return
                            new { Name = p.Name, AddDate = DateTime.Now };

            });

  (3)调节和测试运营能够赢得以下结果:

4858.com 81

4858.com 82

  2.二.贰 原理为王:探二回

  (一)通过Reflector反编写翻译,大家再来看看编写翻译器帮大家调换的东东:

4858.com 83

  (二)看看自动生成的嘱托和方式的概念:

4858.com 84

4858.com 85

  相信经过上节Action的详细分析,那里大家应该也得以举壹反叁精晓编写翻译器帮大家到底做了什么样事儿了,那里笔者就不再赘言了,前边也不会再赘述此方面包车型客车东东(为了节约页面大小)。

  当然,和Action类似,.NET基类库为大家也提供了多达16个输入参数的Func委托,可是,输出参数却唯有1个。

上面已经由此匿名函数完毕了不用阐明类,未来通过C#预订义的寄托项目完毕了不用注解委托项目,那么现在就可以直接写代码片来执行了

叁、重回bool类型的放权委托—Predicate

那三种委托的对应泛型情势,能够写出如下代码:

3.1 初识Predicate

  经过了Func的问询,大家得以知晓接下去的那多个Predicate和Comparison其实都属于有再次回到值类型的委托,他们可是是七个有血有肉的异样实例而已(3个回来bool类型,1个回来int类型)。

MSDN交给的概念:代表定义1组条件并分明内定对象是或不是吻合那一个规范的不二等秘书籍

  它的概念很轻易:(那里就不再对其打开解释了)

public
                            delegate
                                    bool Predicate<in T>(T obj)

  此委托由 Array
和 List<T> 类的三种格局运用,常用于在汇集中寻找成分。

            #region 无参数无返回值
            // lambda实现委托实例
            Action a1 = () => Console.WriteLine(1);
            a1();
            // delegate实现委托实例,下面不再演示delegate。
            Action a2 = delegate { Console.WriteLine(1); };
            a2();
            #endregion

            #region 有参数无返回值
            Action<int> a3 = (x) => Console.WriteLine(x);
            a3(1);
            #endregion

            #region 无参数有返回值的情况
            Func<int> e1= ()=> 1;
            var value1= e1();
            #endregion

            #region 有参数有返回值的情况
            // 最后一个泛型是返回值的类型
            Func<int,int> e2 = (x) => x+1;
            int value2= e2(1);
            // 多参数情况
            Func<int, int,int> e3 = (x,x2) => x+x2 + 1;
            int value3 = e3(1,2);
            #endregion

            #region 返回值是布尔类型
            // 其实Predicate可以用Func返回值是bool来代替的。
            Predicate<int> p = (x) => x > 0;
            bool value4 = p(1);
            #endregion

3.2 深入Predicate

  由于Predicate委托常用于在集合中找出成分,那么大家就来看望哪些使用Predicate委托来进展元素的搜索。于是,我们将眼光转到List集合的FindAll方法,相信大多数童鞋都用过那一个方法。

  (1)先来看看FindAll的概念:

4858.com 86)

                    //


                        // 摘要:

        //     检索与指定谓词定义的条件匹配的所有元素。


        //


                        // 参数:

        //   match:

        //     System.Predicate<T> 委托,用于定义要搜索的元素应满足的条件。


        //


                        // 返回结果:

        //     如果找到,则为一个 System.Collections.Generic.List<T>,其中包含与指定谓词所定义的条件相匹配的所有元素;否则为一个空


        //     System.Collections.Generic.List<T>。


        //


                        // 异常:

        //   System.ArgumentNullException:

        //     match 为 null。


                        public List<T> FindAll(Predicate<T> match);

4858.com 87)

  (二)再来看看FindAll的兑现:

4858.com 88

  (3)未来大家来用一下Predicate委托:还是以十三分PersonList集合为例,假使大家要筛选出Age>20的Person,我们就能够运用FindAll方法。现在大家来写一下以此委托:(前边我们会用Lambda表明式来简写,那才叫二个爽!)能够看来,关键点在于:delegate(Person
p) { return p.Age > 20;
}
这一句上,传入参数是Person类型的靶子,重临的是多个相比结实即bool值。

4858.com 89 View Code

寄托还有部分特色(如可加性),感兴趣的能够查六柱预测关资料。

4、重临int类型的停放委托—Comparison

Expression

4.1 初识Comparison

MSDN交给的定义:代表相比同样品种的七个指标的法子

  它的概念也很简短:

public
                            delegate
                                    int Comparison<in T>(T x, T y)

  T是要比较的目的的门类,而再次回到值是三个有标记整数,提醒 x
与 y 的相对值,如下表所示:

含义

小于 0

x 小于 y。

0

x 等于 y。

大于 0

x 大于 y。

  此委托由 Array
类的 Sort<T>(T[],
Comparison<T>) 方法重载和 List<T> 类的 Sort(Comparison<T>) 方法重载使用,用于对数组或列表中的成分举办排序

Expression指的是System.Linq.Expressions,是Linq表达式,表明式树,不是Lambda表明式!!

4.2 深入Comparison

  由于Comparison委托常用于在集结中开始展览排序,那么大家就来看看哪些行使Comparison委托来拓展元素的排序。于是,大家将眼光转到List集合的Sort方法,相信当先3/6童鞋也都用过这一个办法。

  (一)老惯例,依旧先看看Sort方法的定义:

4858.com 90)

                    //


                        // 摘要:

        //     使用指定的 System.Comparison<T> 对整个 System.Collections.Generic.List<T> 中的元素进行排序。


        //


                        // 参数:

        //   comparison:

        //     比较元素时要使用的 System.Comparison<T>。


        //


                        // 异常:

        //   System.ArgumentNullException:

        //     comparison 为 null。


        //


                    //   System.ArgumentException:

        //     在排序过程中,comparison 的实现会导致错误。
                    例如,将某个项与其自身进行比较时,comparison 可能不返回 0。


                        public
                                void Sort(Comparison<T> comparison);

4858.com 91)

  (二)再来看看Sort方法的达成:

4858.com 92

  能够观察,那里即使选拔Comparison委托但结尾依然转变到了Comparer比较器,再度调用重载的Array.Sort静态方法开展排序。

4858.com 93

  (三)未来大家来用一下Comparison委托:照旧以相当PersonList集合为例,假设我们要以Age为准绳进行降序排列,大家相应怎么来写那一个委托呢?

4858.com 94 View Code

  实现的法力如下图所示:

4858.com 95

  那么,假如是要拓展升序排列呢?只需求改一下:return
p二.Age-p1.Age; 更换一下被减数和减数的职位,就可以落成升序和降序的切换。

4858.com 96 View Code

4858.com 97

Linq表明式用于离线集合的询问。什么是离线集合?最常用的是EF里面的DBContext,小编也只用过这么些,所以笔者不想那么多,就把Expression当成EF里面使用的就行了。

五、Lambda表达式:[ C# 三.0/.NET 三.x 新添特色 ]

  回看,发现上边的代码,须要传1个匿名方法
,写起来尤其别扭。于是大家很想精通能否有简化的语法呢?微软告诉我们:Of
Course,必须有,它就是Lambda表明式。Lambda表达式是比匿名格局更简洁的1种匿名格局语法。

Lambda来源:一九一八年到一九2柒年之间,化学家Alonzo
Church等人发明了拉姆da积分。拉姆da积分是用来表示函数的一套系统,它选拔希腊共和国字母拉姆da(λ)来表示无名函数。近期,函数式编制程序语言(如Lisp)使用那么些术语来代表能够直接描述函数定义的表达式,表明式不再须要盛名字了。

1、只可以承受Lambda表明式表示的委托

5.1 初识Lambda表达式4858.com 98  ⑤.壹.壹 Lambda表明式要点

    1Lambda表达式中的参数列表(参数数量、类型和地方)必须与寄托相相配

    二表明式中的参数列表不必然须求包括类型,除非委托有ref或out关键字(此时必须出示证明);

    ③比方未有参数,必须选取1组空的圆括号

二、Lambda表达式只可以有一条语句。

  5.一.贰 Lambda使用示例

4858.com 99 View Code

   调节和测试运转的结果如下:

4858.com 100

            Expression<Func<int,int>> eeee = (x)=> x+ 1 ;

  5.一.叁 Lambda本质探析

  (一)以上述案例中的Sort方法为例:personList.Sort((p1,
p2) => p1.Age – p2.Age);

  (二)通过反编写翻译工具,能够见见实际是宣称了三个Comparison委托实例:

4858.com 101

  (三)今后,我们来分析一下具体的步凑:有了前边的底子,现在再来看就自在了无数,So
Easy!

    1编写翻译器自动生成了叁个Comparison委托:

4858.com 102

    2编写翻译器帮我们创制了一个顺应Comparison委托具名的静态方法:

4858.com 103

    三实例化Comparison委托变量,并将艺术指针传入该信托;

    4调用List<T>实例的Sort方法,并传播Comparison委托实例;

    在那之中,后面两步壹和贰方可因此反编写翻译后的C#代码获知,而前面两步三和4则须求经过IL代码来分析,前边已经介绍过相关,那里就不再赘述。

总结

一、都以期待得以独立引用1段代码片引起的。
贰、然后引用了委托delegate,delegate的使用办法为注明类型-实例化类型(传入想要使用的类的不2秘技)-使用委托(使用方法)
三、简化操作,使用(通过拉姆da只怕delegate)匿超级模特式来间接声澳优(Karicare)段代码片,防止注解所想使用的类的主意
四、简化操作,直接使用C#预约义的三种委托Action、Func、Predicate,连表明委托都省了。
伍、本文代码:

5.2 回顾Lambda进化史

  前边明白了拉姆da是怎样,那里我们来回想一下Lambda的衍变进程。4858.com 104

  从衍变进度能够领悟,编写翻译器在越来越智能地帮我们做着越来越多的事体,而小编辈却在享用着编写翻译器带来的福利沉浸在高速的开销作用中,变得更其”懒”了。

注意

1、匿名函数总是和嘱托一同使用(三种预约义委托)
二、使用两种预订义委托就能够满意大多数须要。
3、Expression是Linq表明式树,只可以利用接受一句lambda表明式。和匿名函数毫不相关。

5.3 语句Lambda

  Lambda表明式有三种档次:1是拉姆da表明式,贰是语句Lambda。4858.com 105

  那么,语句拉姆da和表明式Lambda到底有啥不相同?

ANSWER:语句Lambda 和
表达式Lambda 的分别在于,前者在
=>右侧有几个语句块(大括号),而后人只有一个表明式(未有return
和大括号)。

EXAMPLES:

(1)表达式Lambda:

list.FindAll(d => d.Id > 2);// goes to


list.ForEach(d => Response.Write(d.ToString() + "<br/>"));

(2)语句Lambda:

list.ForEach(d => { if (d.Id > 2) { Response.Write(d.ToString() + "<br/>"); } });

  可以见到,语句Lambda的右边有八个语句块,在这些大括号内的话语可能会有多条。

 

 

 

 

 

 标准查询运算符提供了席卷筛选、投影、聚合、排序等功用在内的查询作用,其本质是概念在System.Linq.Enumerable类中的50多个为IEnumerable<T>准备的扩大方法

4858.com 106

  从上海教室能够见见,在Enumerable类中提供了数不清的增添方法,那里大家选择之中几个最常用的格局来作一点介绍,使大家能更加好地行使它们。首先,大家需求或多或少数码来举办出现说法:

4858.com 107 View Code

一.1 筛选高手Where方法

  Where方法提供了大家对此贰个集聚的筛选作用,但须要提供多个带bool重临值的”筛选器“(匿超情势、委托、拉姆da表明式均可),从而注脚集合中某些成分是或不是相应被再次来到。那里,大家以地方的数据为例,筛选出集合中具有性别为男,年龄大于20岁的子集合,借助Where方法达成如下:

4858.com 108)

                    static
                            void SQOWhereDemo()

        {

            List<Person> personList = GetPersonList();

 

            List<Person> maleList = personList.Where(p =>

                p.Gender == true && p.Age > 20).ToList();

            maleList.ForEach(m => Console.WriteLine(m.ToString()));

        }

4858.com 109)

  (一)运维结果如下图所示:

4858.com 110

  (二)由本体系文章的第三篇可见,扩张方法的本色是在运营时调用扩张类的静态方法,而小编辈写的Lambda表明式在编译时又会被转为匿有名的模特式(准确地说应该是预约义泛型委托实例)作为艺术参数字传送入扩张方法中,最终调用实行该扩大方法生成三个新的List集合再次回到。

一.二 投影大牌Select方法

  Select方法能够查询投射,再次回到新对象集合。那里,假若我们先筛选出装有男性集合,再依据男性集合中具备项的姓名生成子集合(那是三个分化于原项指标类型),就能够借助Select方法来促成。

4858.com 111)

                    static
                            void SQOSelectDemo()

        {

            List<Person> personList = GetPersonList();

 

            List<LitePerson> liteList = personList.Where(p =>

                p.Gender == true).Select(

                p => new LitePerson() { Name = p.Name }).ToList();

            liteList.ForEach(p => Console.WriteLine(p.ToString()));

        }

4858.com 112)

  (壹)运维结果如下图所示:

4858.com 113

  (2)这里也得以使用匿名类,能够省去事先表明LitePerson类的步凑,但需求相配var使用:

var annoyList = personList.Where(p =>

    p.Gender == true).Select(

    p => new { Name = p.Name }).ToList();

  (3)这里因为达成LitePerson类重写了ToString()方法,所以那里直接调用了ToString()方法。

壹.三 排序小生OrderBy方法

  聊起排序,咱们及时想起了SQL中的order
by语句,而正规查询运算符中也为大家提供了OrderBy这一个情势,值得1提的就是大家能够实行多规格的排序,因为OrderBy方法重返的如故是2个IEnumerable<T>的连串,依然可以承袭运用扩张方法。但要注意的是,第2回应该选取ThenBy方法。

4858.com 114)

                    static
                            void SQOOrderByDemo()

        {

            List<Person> personList = GetPersonList();

                        // 单条件升序排序


            Console.WriteLine("Order by Age ascending:");

            List<Person> orderedList = personList.OrderBy(p => p.Age).ToList();

            orderedList.ForEach(p => Console.WriteLine(p.ToString()));

                        // 单条件降序排序


            Console.WriteLine("Order by Age descending:");

            orderedList = personList.OrderByDescending(p => p.Age).ToList();

            orderedList.ForEach(p => Console.WriteLine(p.ToString()));

                        // 多条件综合排序


            Console.WriteLine("Order by Age ascending and ID descending:");

            orderedList = personList.OrderBy(p => p.Age)

                .ThenByDescending(p => p.ID).ToList();

            orderedList.ForEach(p => Console.WriteLine(p.ToString()));

        }

4858.com 115)

  运营结果如下图所示:

4858.com 116

一.四 连接道士Join方法

  在数据库中,大家对三个表或五个表进行连接查询时一再会用到join语句,然后钦赐多少个表之间的关联关系(例如:
a.bid =
b.aid)。在行业内部查询运算符中,细心的.NET基类库也为大家提供了Join方法。未来,假诺大家有多个类:Person和Children,当中每一种Children对象都有一个ParentID,对应Person对象的ID,现需求打印出富有Person和Children的音信,能够凭借Join方法来促成。

4858.com 117)

                    static
                            void SQOJoinDemo()

        {

            List<Person> personList = GetPersonList();

            List<Children> childrenList = GetChildrenList();

 

                        // 连接查询


                    var joinedList = personList.Join(childrenList,

                p => p.ID, c => c.ParentID, (p, c) => new


                {

                    ParentID = p.ID,

                    ChildID = c.ChildID,

                    ParentName = p.Name,

                    ChildName = c.ChildName

                }).ToList();

            joinedList.ForEach(c => Console.WriteLine(c.ToString()));

        }

4858.com 118)

  运行结果如下图所示:

4858.com 119

一.伍 分组老师GroupBy方法

  在数据库中,我们要对查询结果开展分组会用到
group by
语句,在标准查询运算符中,大家也有相应的GroupBy方法。那里,倘使大家对Person数据集依照性别实行分拣,该怎么来写代码呢?

4858.com 120)

                    static
                            void SQOGroupByDemo()

        {

            List<Person> personList = GetPersonList();

 

            IEnumerable<IGrouping<bool, Person>> groups =

                personList.GroupBy(p => p.Gender);

            IList<IGrouping<bool, Person>> groupList = groups.ToList();

 

                    foreach (IGrouping<bool, Person> group in groupList)

            {

                Console.WriteLine("Group:{0}", group.Key ? "男" : "女");

                    foreach (Person p in group)

                {

                    Console.WriteLine(p.ToString());

                }

            }

        }

4858.com 121)

  (一)那里要求小心的是:通过GroupBy方法后回去的是一个IEnumerable<IGrouping<TKey,
TSource>>
品类,当中TKey是分组依照的品类,那里是基于Gender来分组的,而Gender又是bool类型,所以TKey那里为bool类型。TSource则是分组之后依次要素的门类,那里是将List<Person>集合进行分组,由此分完组后各种成分都存款和储蓄的是Person类型,所以TSource那里为Person类型,Do
you understand now?

  (二)运转结果如下图所示:

4858.com 122

  (三)或然有人会说笔者咋记得住GroupBy重回的充裕类型,太长了,小编也不想记。如何是好呢?不怕,我们可以接纳var关键字嘛:

4858.com 123)

                    var annoyGroups = personList.GroupBy(p => p.Name).ToList();

                    foreach (var group in annoyGroups)

            {

                Console.WriteLine("Group:{0}", group.Key);

                    foreach (var p in group)

                {

                    Console.WriteLine(p.ToString());

                }

            }

4858.com 124)

一.陆 分页实战Skip与Take方法

  相信广大人都选用过正式查询运算符实行分页操作,那里大家再度来看看哪些借助Skip与Take方法来贯彻分页操作。依然以PersonList集合为例,若是页面上的报表每页展现5条数据,该怎么来写代码呢?

4858.com 125)

                    static
                            void SQOPagedDemo()

        {

                        // 这里假设每页5行数据


            // 第一页


            Console.WriteLine("First Page:");

                    var firstPageData = GetPagedListByIndex(1, 5);

            firstPageData.ForEach(d => Console.WriteLine(d.ToString()));

                        // 第二页


            Console.WriteLine("Second Page:");

                    var secondPageData = GetPagedListByIndex(2, 5);

            secondPageData.ForEach(d => Console.WriteLine(d.ToString()));

                        // 第三页


            Console.WriteLine("Third Page:");

                    var thirdPageData = GetPagedListByIndex(3, 5);

            thirdPageData.ForEach(d => Console.WriteLine(d.ToString()));

        }

 

                    static List<Person> GetPagedListByIndex(int pageIndex, int pageSize)

        {

            List<Person> dataList = GetMorePersonList();

                    return dataList.Skip((pageIndex - 1) * pageSize)

                .Take(pageSize).ToList();

        }

4858.com 126)

  运营结果如下图所示:

4858.com 127

一.7 浅谈延迟加载与当时加载

  (1)延期加载(Lazy
Loading):
除非在大家要求多少的时候才去数据库读取加载它。

  在正规查询运算符中,Where方法就是三个第一名的延期加载案例。在实际上的开垦中,大家一再会接纳1些OOdysseyM框架例如EF去操作数据库,Where方法的应用则是每一回调用都只是在接二连三生成SQL语句时增添3个询问条件,EF不能分明此次查询是或不是曾经增多停止,所以未有章程木有办法在各种Where方法推行的时候明确最终的SQL语句,只可以回到1个DbQuery对象,当使用到这一个DbQuery对象的时候,才会依照全部条件转移最后的SQL语句去查询数据库。

    var searchResult = personList.Where(p =>

          p.Gender == false).Where(p => p.Age > 20)

          .Where(p=>p.Name.Contains("奶茶"));

4858.com,  (2)眼看加载(Eager
Loading):
加载数据时就把该指标相关联的别的表的多少共同加载到内部存款和储蓄器目的中去。

  在标准查询运算符中,FindAll方法正是两个出色的即时加载案例。与延迟加载相对应,在付出中只要利用FindAll方法,EF会依照章程中的条件自动生成SQL语句,然后立刻与数据库举行互动获取查询结果,并加载到内部存款和储蓄器中去。

                    var searchResult = personList.FindAll(p=>p.Gender == false


                && p.Name.Contains("奶茶"));

2、查询办法何人越来越快?LINQ:[ C# 叁.0/.NET 三.x 新扩张特色 ]

贰.一 初识LINQ:类似SQL风格的代码

  LINQ又称语言集成查询,它是C#
叁.0的新语法。在更加多的人看来,它是1种有益的询问表明式,可能说是和SQL风格周边的代码

                    var maleList = from p in personList

                    where p.Gender == true


                        select p;

  (一)LINQ表达式以”from”起始,以”select
或 group by子句”结尾;

  (二)LINQ表达式的出口是三个IEnumerable<T> 或 IQueryable<T> 集合;(注:T 的品类 由
select 或 group by 预计出来)

2.二 LINQ使用:完结除Skip和Take外的正式查询运算符的法力

  (一)基本标准查询:

4858.com 128

4858.com 129)

            List<Person> personList = GetPersonList();

            List<Children> childList = GetChildrenList();

                        // 基本条件查询


            Console.WriteLine("Basic Query:");

                    var maleList = from p in personList

                    where p.Gender == true


                    select p;

            maleList.ToList().ForEach(m =>

                Console.WriteLine(m.ToString()));

4858.com 130)

  (二)排序条件查询:

4858.com 131

4858.com 132)

                        // 排序条件查询


            Console.WriteLine("Order Query:");

                    var orderedList = from p in personList

                    orderby p.Age descending

                    orderby p.Name ascending

                    select p;

            orderedList.ToList().ForEach(m =>

                Console.WriteLine(m.ToString()));

4858.com 133)

  (三)连接查询:

4858.com 134

4858.com 135)

                        // Join连接查询


            Console.WriteLine("Join Query:");

                    var joinedList = from p in personList

                             join c in childList

                             on p.ID equals c.ParentID

                    select
                            new


                             {

                                 Person = p,

                                 Child = c

                             };

                    foreach (var item in joinedList)

            {

                Console.WriteLine(item.ToString());

            }

4858.com 136)

  (四)分组查询:

4858.com 137

4858.com 138)

                        // 分组条件查询


            Console.WriteLine("Group Query:");

                    var groupList = from p in personList

                            group p by p.Gender;

                    foreach (var group in groupList)

            {

                Console.WriteLine("Group:{0}", 

                    group.Key? "男":"女");

                    foreach(var item in group)

                {

                    Console.WriteLine(item.ToString());

                }

            }

4858.com 139)

  运转结果请参见上一节标准查询运算符中相关的周转结果,或下载附属类小部件运维查看,那里不再贴图。

2.3 LINQ本质:生成对应的正儿8经查询运算符

  作为3个细密的.Net码农,大家情难自禁对LINQ表明式为我们做了如何工作而感叹?于是,大家又忆起了我们的”滑高筒靴”—Reflector或ILSpy,去探望编写翻译器为大家做了怎样事!

  (1)以上述的宗旨原则查询代码为例,大家看来原来编写翻译器将LINQ生成了对应的正儿8经查询运算符,即Where扩展方法:

4858.com 140

  (2)再来看看排序条件查询的代码,也是生成了对应的专业查询运算符,即OrderBy扩大方法:

4858.com 141

  (3)总结:LINQ编写翻译后会生成对应的正规化查询运算符(查询->Where,排序->OrderBy,连接->Join,分组->GroupBy),所以LINQ表明式其实便是近乎于SQL风格的壹种越发团结语法糖而已。其本质照旧扩充方法、泛型委托等”旧酒”,被三个”新瓶子”所包装了4起,就变得高大上了。

多重计算

  弹指,四篇文章的牵线就到此甘休了,其实本类别介绍的都是不算新语法,其实也足以说成是老语法了。说它们新,只不过是争辩于.NET老版本来说,而且常常支付中山大学家有希望没有注意到的1部分细节,本连串做了三个简短的介绍。这几天看到不少田园里的童鞋起始关心C#
陆.0的新特点了,粗略看了看,语法糖居多,相信通过了这一密密麻麻的探秘,对于新的语法糖,我们得以站在多个相比较高的冲天去对待它们。最终,多谢各位园友的浏览,以及给自个儿的有个别鞭策,再一次感激!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有