LINQ之路 3:C# 3.0的语言功能(下)

在LINQ介绍一篇中,我们已经看到了隐式类型变量var,扩展方法(Extension method)和Lambda表达式的身影。没错,他们正是LINQ技术的基石,是他们让LINQ的实现成为可能,并且简化了LINQ表达式的书写。在这一篇中,我将和大家一一探讨C#3.0在语言功能上所作的努力,包括:扩展方法、Lambda表达式和对象初始化器。

扩展方法

下一个与LINQ密切相关的C# 3.0语言功能是扩展方法(Extension method)。在这之前,一旦一个类型被编译进.NET程序集后,我们便不能再修改该类型的定义了。为该类型添加、修改、删除成员的唯一办法就是修改类型的定义代码。

但有时候,当需要为类型添加新功能但并不拥有类型的已有代码时,比如,我们想要为.NET库类型List添加自定义的dump方法时,该怎么做呢,答案是扩展方法。扩展方法允许在不修改类型定义的情况下,让该类型获得功能上的扩展。

定义扩展方法

当定义一个扩展方法时,第一个限制就是必须把方法定义在静态类中,因此每一个扩展方法也必须声明为静态的。第二个限制是扩展方法要用this关键字对第一个参数进行修饰,这个参数也就是我们希望进行扩展的类型。

比如下面的扩展方法允许.NET基类库中的所有对象都拥有全新的方法DisplayDefiningAssembly()。

        static class MyExtensions        {            // 本方法允许任何对象显示它所处的程序集            public static void DisplayDefiningAssemlby(this object obj)            {                Console.WriteLine("{0} is defined in: \n\t {1}\n",                    obj.GetType().Name,                    System.Reflection.Assembly.GetAssembly(obj.GetType()));            }        }

调用扩展方法

我们有两种方式来使用扩展方法,第一种是在实例层次上调用扩展方法,第二种是静态调用扩展方法。

        public void UsingExtensionMethods()        {            int myInt = 12345;

            // 1. 在实例层次上调用扩展方法            myInt.DisplayDefiningAssemlby();

            // 2. 静态调用扩展方法            MyExtensions.DisplayDefiningAssemlby(myInt);        }

实例上,通过一个对象调用它的扩展方法只是编译器的烟幕弹效果而已,背后编译器会转换成静态方法的调用。

其他注意事项

上面说到,扩展方法本质上是可以从扩展类型的实例上调用的静态方法。所以它和普通的方法是不一样的,扩展方法不能直接访问扩展类型的成员,从另外一个角度讲,扩展方法即不是直接修改,也不是继承。

另外一个要注意的地方是:虽然表面上扩展方法是全局的,但其实他们受制于所处的命名空间,要使用在其他命名空间中定义的扩展方法时,我们首先需要导入该命名空间。

Lambda表达式

Lambda表达式的引入是与委托类型的使用密切相关的,本质上,Lambda表达式只是用更简单的方式来书写匿名方法,从而彻底简化.NET委托类型的使用。下面我们一步一步的来看看Lambda表达式的简化之路:

实例找出整数List<T>中的偶数,我们调用了List<T>类型的FindALl()方法,这个方法需要System.Predicate<T>泛型委托,它用于接受类型为T的输入参数并返回一个布尔值。

传统的委托使用方式

传统的委托使用方式会为委托目标定义一个单独的方法,如下:

        public static void TraditionalDelegateSyntax()        {            List<int> list = new List<int>();            list.AddRange(new int[] { 1, 5, 10, 20 ,33 });

            //使用传统委托语法调用FindAll            Predicate<int> callback = new Predicate<int>(IsEvenNumber);            List<int> evenNumbers = list.FindAll(callback);

            foreach (int num in evenNumbers)                Console.Write("{0}\t", num);            //Output:   10    20        }

        // Predicate<>委托的目标        static bool IsEvenNumber(int i)        {            return (i % 2) == 0;        }

匿名方法取代显示的委托函数

这种方式让我们不再需要完整的方法定义,对于一些专门为了委托而定义的函数而言是一个很大的简化,如下:

        public static void AnonymousMethodSyntax()        {            List<int> list = new List<int>();            list.AddRange(new int[] { 1, 5, 10, 20, 33 });

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

            foreach (int num in evenNumbers)                Console.Write("{0}\t", num);            //Output:   10    20        }

Lambda表达式

Lambda表达式让我们进一步简化FindAll()的调用,使用新的语法时,底层的委托语法消失得无影无踪,如下所示:

        public static void LambdaExpressionSyntax()        {            List<int> list = new List<int>();            list.AddRange(new int[] { 1, 5, 10, 20, 33 });

            //使用Lambda表达式            List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);

            foreach (int num in evenNumbers)                Console.Write("{0}\t", num);            //Output:   10    20        }

Lambda表达式可以应用于任何匿名方法可以应用的场合,而且比匿名方法更加简洁更节省编码时间。其实C#编译器只是把Lambda表达式翻译为相应的普通匿名方法而已。

Lambda表达式的格式:先定义参数列表,”=>”标记(可读为:goes to)紧随其后,然后是表达式。即:ArgumentsToProcess => StatementsToProcessThem

Lambda表达式的参数可以是显示类型化的也可以是隐式类型化的。比如上例中的参数i就是隐式类型化的,我们也可以写为如下:

            // 显示定义参数的类型            List<int> evenNumbers = list.FindAll((int i) => (i % 2) == 0);

Lambda表达式也可以是一个代码块,其中包含多条代码语句,用花括号括起来即可:

            // 使用语句块编写Lambda表达式            List<int> evenNumbers = list.FindAll((int i) =>                {                    Console.WriteLine("processing value: {0}", i);                    bool isEven = (i % 2) == 0;                    return isEven;                });

对象初始化器

C# 3.0提供的 对象初始化器语法用来初始化新类或新结构变量的状态。使用这种语法,我们可以以一种非常简洁的方式来创建对象和为对象的属性赋值。如下:

    public class Point    {        public Point() { }        public Point(int x, int y)        {            X = x;            Y = y;        }

        public int X { get; set; }        public int Y { get; set; }    }

    static void ObjectInitSyntax()    {        // 手动初始化各属性        Point aPoint = new Point();        aPoint.X = 10;        aPoint.Y = 20;

        // 使用新的对象初始化语法进行初始化        Point bPoint = new Point { X = 10, Y = 20 };    }

使用初始化语法调用构造函数

上面的示例中,对象初始化语法会隐式调用默认的构造函数初始化Point实例,而且我们还可以显示调用定制的构造函数,如下:

        static void ObjectInitSyntax()        {            // 在这里,默认构造函数被隐式调用            Point bPoint = new Point { X = 10, Y = 20 };

            // 我们也可以显示调用默认构造函数            Point cPoint = new Point() { X = 10, Y = 20 };

            // 我们还可以调用自定义的构造函数,只是这里1, 2会被10, 20覆盖            Point dPoint = new Point(1, 2) { X = 10, Y = 20 };        }

初始化内部类型

当我们用这种语法来初始化一个“复杂”的对象时,其优点会更具说服力,假如我们有类Rectangle如下,可以明显的看出,对象初始化语法不但大大减少了我们敲打键盘的次数,也更加的简洁明了。

    public class Rectangle    {        public Point TopLeft { get; set; }        public Point BottomRight { get; set; }    }

    static void CompareObjectInitMethods()    {        // 传统初始化方法        Rectangle r = new Rectangle();        Point p1 = new Point();        p1.X = 10;        p1.Y = 10;        r.TopLeft = p1;        Point p2 = new Point();        p2.X = 20;        p2.Y = 20;        r.BottomRight = p2;

        // 对象初始化语法        Rectangle r2 = new Rectangle        {            TopLeft = new Point { X = 10, Y = 10 },            BottomRight = new Point { X =   20, Y = 20 }        };    }

集合的初始化

集合初始化语法非常类似于对象初始化语法,它使得我们可以像初始化普通数组一样初始化容器(如ArrayList或List<T>)。

        static void CollectionInitSyntax()        {            // 初始化标准数组            int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

            // 初始化一个ArrayList            ArrayList list = new ArrayList { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

            // 初始化一个List<T>泛型容器            List<int> list2 = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

            // 如果容器存放的是非简单对象            List<Point> pointList = new List<Point>            {                new Point { X = 2, Y = 2},                new Point { X = 3, Y = 3}            };

            // 使用恰当的缩进和嵌套的大括号会使代码易于阅读,同时节省我们的输入时间            // 想想如果不使用初始化语法构造如下的List,将需要多少行代码            List<Rectangle> rectList = new List<Rectangle>            {                new Rectangle { TopLeft = new Point { X = 1, Y = 1},                    BottomRight = new Point { X = 2, Y = 2}},                new Rectangle { TopLeft = new Point { X = 3, Y = 3},                    BottomRight = new Point { X = 4, Y = 4}},                new Rectangle { TopLeft = new Point { X = 5, Y = 5},                    BottomRight = new Point { X = 6, Y = 6}}            };        }


 

时间: 2024-09-30 14:43:17

LINQ之路 3:C# 3.0的语言功能(下)的相关文章

LINQ之路 2:C# 3.0的语言功能(上)

在上一篇的LINQ介绍中,我们已经看到了隐式类型变量var,扩展方法(extension method)和lambda表达式的身影.没错,他们正是LINQ技术的基石,是他们让LINQ的实现成为可能,并且简化了LINQ表达式的书写.在这一篇中,我将和大家一一探讨C#3.0在语言功能上所作的努力,包括:隐式类型局部变量.自动属性和匿名类型. 隐式类型局部变量 C#是强类型语言,意味着我们在声明变量时必须指定变量的具体类型,比如: static void DeclareExplicitVars() {

LINQ之路 4:LINQ方法语法

书写LINQ查询时又两种语法可供选择:方法语法(Fluent Syntax)和查询语法(Query Expression). LINQ方法语法是非常灵活和重要的,我们在这里将描述使用链接查询运算符的方式来创建复杂的查询,方法语法的本质是通过扩展方法和Lambda表达式来创建查询.C# 3.0对于LINQ表达式还引入了声明式的查询语法,通过查询语法写出的查询比较类似于SQL查询.本篇会对LINQ方法语法进行详细的介绍. 当然,.NET公共语言运行库(CLR)并不具有查询语法的概念.所以,编译器会在

LINQ之路 7:子查询、创建策略和数据转换

在前面的系列中,我们已经讨论了LINQ简单查询的大部分特性,了解了LINQ的支持计术和语法形式.至此,我们应该可以创建出大部分相对简单的LINQ查询.在本篇中,除了对前面的知识做个简单的总结,还会介绍几种创建更复杂查询的方式,让我们在面对更复杂的场景时也能轻松面对,包括:子查询.创建策略和数据转换. 子查询 在创建一个复杂的查询时,通常我们需要用到子查询.相信大家都记得SQL查询里的子查询,在创建LINQ查询时也是如此.在LINQ中,对于方法语法,一个子查询包含在另外一个查询的lambda表达式

Ubuntu14.0.4系统下 SVN的安装与配置

Ubuntu14.0.4系统下  SVN的安装与配置 SVN(subversion)是近年来崛起的版本管理工具,绝大多数开源软件都使用了svn作为代码版本管理软件.对于写代码的用户来说使用版本控制管理工具svn来说最合适 不过了,下面就介绍一下在Ubuntu 14.0.4下安装和配置SVN的简要方法! 本文是小编亲自整理.测试.验证过的方法,也可以算是最全.最简易的SVN安装配置方法! 下面文档分为四个部分: 1.在Ubuntu 14.0.4系统中安装SVN 2.配置SVN 3.启动和关闭svn

cocos2d-x 3.0在mac下的基本配置

随着3.0正式版的发布,有必要开始对其进行一番研究.首先就从环境搭建以及基本的配置开始. 其实在cocos2d-x的官方wiki中已经有了比较详细的介绍,只不过是英文的,这里针对实际操作给大家简单翻译说明一下. 创建一个cocos2d-x项目 1.1 环境需要 Android 2.3 或更新版本 ios 5.0 或更新版本 OS X 10.7 或更新版本 Xcode 4.6 以上版本 gcc 4.7 和Android ndk-r9以上版本 Python 2.7.5 1.2 创建一个新的项目 首先

QWT6.0.1+win7下安装说明

A) 简介 1.QWT是一个基于LGPL版权协议的开源项目, 可生成各种统计图.它为具有技术专业背景的程序提供GUI组件和一组实用类,其目标是以基于2D方式的窗体部件来显示数据, 数据源以数值,数组或一组浮点数等方式提供, 输出方式可以是Curves(曲线),Slider(滚动条),Dials(圆盘),Compasses(仪表盘)等等.该工具库基于Qt开发,所以也继承了Qt的跨平台特性. 2.QWT官方网址:http://qwt.sourceforge.net/ QWT6.0.1官方下载地址:h

yii2.0 访问控制器下的方法时出现 Object Not Found! 解决办法

yii2.0  访问控制器下的方法时出现 Object Not Found! 时 可以查看(apache)  入口文件index.php 的同级有没有 .htaccess 文件 没有.htaccess文件  要添加该文件 内容: RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php 保存后重启apache

thinkphp 5.0 lnmp环境下 无法访问,报错500(public目录)

两种方法: 1.修改fastcgi的配置文件 /usr/local/nginx/conf/fastcgi.conf fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/../:/tmp/:/proc/"; 2.这个时候需要在php.ini下面添加如下内容 注: xxx.abczn.com 替换成你对应的域名和目录 [HOST=xxx.abczn.com]open_basedir=/home/wwwroot/xxx.abc

MongoDB4.0在windows10下的安装与服务配置

本地安装及网页测试 在官网下载最新的安装文件 下载地址 : https://www.mongodb.com/download-center#community 可以在MongoDB官网选择Community Server版本下载,但是它似乎经常没有响应.可以在这里直接选择需要的版本下载,要在Windows下安装可以直接选msi安装文件. 安装msi文件 下载好后,一致next,在中间一步选择 custom 选项,以选定自己喜好的安装位置 修改安装路径. 这个MSI文件有问题,这里必须不能改动,直