【LINQ技术】扩展特性和LINQ操作符

LINQ特有的编程结构

LINQ就像是嵌入到C#中的强类型查询语言,尽管和SQL查询很像,但语法却并不相同,甚至还有截然相反的一面。

LINQ是在.NET发展到3.5版的时候被引进的,C#和VB语言都为此做了许多工作,扩展了大量新的编程结构。

一、隐式类型本地变量

var——一个如此小巧的关键字却有着强大的力量。

var varInt=1;
var varBool=True;
var varString="String, String, String";

Console.WriteLine("varInt is a: {0}",varInt.GetType().Name);
Console.WriteLine("varBool is a: {0}",varBool.GetType().Name);
Console.WriteLine("varString is a: {0}",varString.GetType().Name);

上面的代码会很神奇的自动显示出它们各自的类型。

var的限制:

1.var不能用于字段数据
2.var不能用于返回值或参数类型
3.必须在声明时分配值,且值不为NULL

但var可以这样:

var alarmClock=new AlarmClock();
alarmClock=null;
var varInt=1;
var varInt2=varInt;

bool varBool=True;
var varBool2=varBool;
static int Alarm()
{
    var alarm="09:20";
    return alarm;
}

隐式类型数据是强类型数据。类型推断延续了C#语言的强类型特性,并且只会在编译时影响变量的声明。之后,该数据点被视为它声明的类型。为该变量分配不同的类型将导致编译时错误。

// 编译器知道“s”是一个string类型
var s="This is a string.";
s="Funny...";

// 因此可以调用string的所有成员
string bigS=s.ToUpper();

// 但不能将非string类型的数据分配给s
s=True; 

var为LINQ而生

LINQ技术使用的是查询表达式,它可以根据表达式本身的格式产生动态创建的结果集。但有时在某些情况下根本无法显示定义查询的访问类型,这时隐式类型就会发挥作用了。

二、对象和集合初始化语法

在扩展这个新的特性之前,我们要创建一个对象并给其属性初始化会是这样:

var rect = new Rect();
rect.Height=100;
rect.Width=200;

但是支持C# 3.0加入了这个新特性,代码就成了这样:

var rect = new Rect(){Height=100,Width=200};

如果有构造函数的话还可以在括号内传入参数呢,就像这样:

var rect = new Rect("bigRect"){Height=100,Width=200};

如果对于集合,那就优势就更加明显了。

List<Alarm> alarmList = new List<Alarm>
{
    new Alarm { Name = "todayAlarm", Description=new Desc{ Time="07:20",Location="Home"}},
    new Alarm { Name = "tomorrowAlarm", Description=new Desc{
Time="08:40",Location="Company"}},
    new Alarm { Name = "nextYearAlarm", Description=new Desc{
Time="18:40",Location="Shanghai"}},
};

将对象/集合初始化语法和隐式类型本地变量相结合就可以声明匿名类型。

三、匿名类型

通过这个特性可以快速建立数据的“结构”,编译器将根据名称/值对的集合在编译时生成的类。该类型是基于值的语义构建的,因此System.Object中的每个虚方法都要重写。要定义一个匿名类型,可以声明一个隐式类型变量,并使用对象初始化语法指定数据的结构。

var alarm=new
{
    currentTime=DateTime.Now,
    alarm=new {Name="todayAlarm",Location="Shanghai",Time="18:20"},
};

四、扩展方法

通过面向对象的继承机制,我们可以给一个类添加新的方法等,但这不是唯一的方法。

C#的扩展方法不用子类就能向已知类型中添加新的功能,当然了,它还可以向不能有子类的密封类和结构中添加新的功能。但是需要注意的是:

1.在写扩展方法时,第一个参数必须使用this限定符,用来表示被扩展的类型
2.第一个参数不能有ref或者out的修饰符
3.第一参数还不能是指针类型
2.扩展方法只能存在于静态类中,并且必须使用static关键字将方法声明为静态的

而且使用扩展方法并不会影响性能,因为这些都是编译器需要做的,而通过继承则需要影响性能。

五、Lambda表达式

Lambda表达式可算是Lisp语言的核心了,如果想要了解该语言可以访问我的其他博客。C#添加了这个特性可谓是有了质的提升。

Lambda大大简化了.NET委托的使用,减少了需要手工输入的代码。

List<int> list=new List<int>();
list.AddRange(new int[]{10,21,4,8,3,59});

List<int> list2=list.FindAll(i=>(i%2)==1);

Console.WriteLine("Here are your odd numbers:");
foreach(int n in list2)
{
    Console.Write("{0}\t",n);
}

C# LINQ查询操作符是调用System.Linq.Enumerable类中方法的简便方式,这些方法通常都使用委托作为参数,用来处理数据生成正确的结果集。

LINQ的用途

1.数据

作为软件开发者,编程的绝大部分时间中都在操作着数据。数据有哪些来源?我们可能会从用户输入中得到数据,也可能从配置文件中得到数据,还可能从网络中得到数据,甚至可能从WCF服务返回的内存中得到数据。但是在操作特定的数据时,我们往往会使用不同的API。

数据 操作数据的方式
关系数据 System.Data.dll和System.Data.SqlClient.dll等
XML文档数据 System.Xml.dll
元数据表 System.Reflection命名空间
对象集合 System.Array和System.Collections/System.Collections.Generic

我们可以使用ADO.NET、XML命名空间、Reflection(反射)服务还有各种对于集合的操作。但就这些API本身而言,它们都是独立的个体。而LINQ API则倾向于提供一个同一且对称的方式,以便我们能够在广义的数据上得到和操作“数据”。通过使用LINQ,我们便可以直接创建被称为查询表达式(query expression)的实体。这些查询表达式是基于许多查询操作符(query operator)的,而且有意设计为类似SQL表达式。

而根据LINQ查询的应用场景,可以分为以下5个部分:

LINQ to Object: 针对数组和集合使用的LINQ查询
LINQ to XML: 使用LINQ来操纵和查询XML文档
LINQ to DataSet: 针对ADO.NET DataSet对象使用的LINQ查询
LINQ to Entity: 对ADO.NET Entity Framework (EF)API使用的LINQ查询
Parallel LINQ (PLINQ): 并行处理LINQ查询返回的结果

2.LINQ表达式是强类型的

和传统的SQL语句不同,LINQ查询表达式是强类型的,所以我们必须保证这些表达式在语法上都是合理的。因此我们要充分利用Visual Studio这个IDE的智能感知、自动感知等有用的功能。

LINQ操作数组

通过下面这个示例,我们可以将数组中的int数值取出奇数并排序。

static void Main(string[] args)
{
    LINQDemo();
}

static void LINQDemo()
{
    int[] array = { 8, 30, 13, 35, 89, 31, 83, 58, 32, 76 };
    IEnumerable<int> subset = from a in array
                    where a % 2 == 1
                    orderby a
                    select a;
    foreach(int i in subset)
    {
         Console.Write("Item: {0}\n", i);
    }
}

最后获得的结果的集合是由一个实现了IEnumerable< T >泛型版本的对象来表示的。前面我们介绍了var,这里可以用吗?当然可以。

var subset = from a in array
    where a % 2 == 1
    orderby a
    select a;

如果使用var的话,那么在foreach中也需要将int改为var。一般来说,最好在获取LINQ查询结果时都使用隐式类型,但在绝大多是情况下,真正的返回值是实现了IEnumerable< T >接口的类型。

延迟执行和立即执行

在迭代内容之前,LINQ查询表达式并不会真正进行计算。这就叫“延迟执行”,它能够让相同的容器执行多次相同的LINQ查询,而始终获得最新的结果。

static void LINQDemo()
{
    int[] array = { 8, 30, 13, 35, 89, 31, 83, 58, 32, 76 };
    var subset = from a in array
                    where a % 2 == 1
                    orderby a
                    select a;
    foreach(var i in subset)
    {
         Console.Write("Item: {0}\n", i);
    }
    Console.WriteLine();

    array[0]=71;

    foreach(var i in subset)
    {
         Console.Write("Item: {0}\n", i);
    }
    Console.WriteLine();
}

这样一来在第二次的输出中就会在数组的头部添加一个71。

这里就是在foreach中运算的LINQ表达式,但如果希望在foreach之前就运算呢?可以调用Enumerable类型定义的许多扩展方法。它定义了ToArray< T >()、ToList< T >()和ToDictionary

static void LINQDemo()
{
    int[] array = { 8, 30, 13, 35, 89, 31, 83, 58, 32, 76 };
    int[] subsetInt = (from a in array
                    where a % 2 == 1
                    orderby a
                    select a).ToArray<int>();
    List<int> subsetList = (from a in array
                    where a % 2 == 1
                    orderby a
                    select a).ToList<int>();
}

记得将整个LINQ表达式用圆括号括起来,这样就能将它强制转换为正确的实际类型来调用Enumerable的扩展方法。

C#编译器不得不说真的很强大,它能够准确的检测泛型项的类型参数,我们不需要指定类型参数。因此也可以像下面这样:

int[] subsetInt = (from a in array
                    where a % 2 == 1
                    orderby a
                    select a).ToArray();

立即执行的好处会体现在当要对外部调用者返回LINQ查询时。

LINQ 查询操作符

查询操作符 含义
from、in 用于定义任何LINQ表达式的主干,允许从合适的容器中提取数据子集
where 用于定义从一个容器里取出哪些项的限制条件
select 用于从容器中选择一个序列
join、on、equals、into 基于指定的键来做关联操作,但这些“关联”不必与关系数据库的数据有什么关系
orderby、ascending、descending 允许结果子集按升序或降序排序
group、by 用特定的值来对数据分组后得到一个子集

1.获取数据子集

使用where操作符可以从数据容器里得到特定的子集,where后应该是运算结果为布尔值的表达式。当然了,对于&& 和 || 这些操作也都是可以得。

2.投影新的数据类型

如果想要传入的Alarm[]类型的alarm集合中得到一个只有时间和名字的结果集,可以定义一个select语句,动态生成一个新的匿名类型。

static void GetNameAndTime(Alarm[] alarm)
{
    var alarmSubset=from a in alarm select new {a.Name,a.Time};
    foreach (var i in alarmSubset)
    {
        Console.WriteLine(i.ToString());
    }
}

因为在使用投影的LINQ查询时,我们无法知道实际的数据类型,因为它是在编译时决定的,所以就必须使用var关键字。因此也就无法在创建方法时返回隐式类型。

但如果需要返回这些数据呢,难道就没有办法吗?可以用前面介绍的ToArray()扩展方法将查询结果转换为.NET System.Array对象。

static Array GetAlarmProp(Alarm[] alarm)
{
    var alarmSubset=from a in alarm select new {a.Name,a.Time};
    return alarmSubset.ToArray();
}

最后我们可以在Main()中如下调用和处理数据:

Array al=GetAlarmProp(alarm);
foreach( object o in al)
{
    Console.WriteLine(0);
}

3.获取数据集中的个数

在投影一批新的数据时,比如在1000个Computer中获取所有的Windows操作系统的,那么如果知道它们的总数呢?

int pc=(from c in computer where c.operation="Windows" select c).Count();

4.反转结果集

想要将最终的结果进行反转,可以通过Enumerable类中的扩展方法Reverse< T >()对结果集中的项进行反转。

static void GetNameAndTime(Alarm[] alarm)
{
    var alarmSubset=from a in alarm select new {a.Name,a.Time};
    foreach (var i in alarmSubset.Reverse())
    {
        Console.WriteLine(i.ToString());
    }
}

5.对表达式进行排序

在前面我们曾使用orderby对获取到的奇数进行排序,那么为什么直接用“orderby a”就可以排序呢?很简单,因为默认是正序……对于字符串是按字母顺序,对数字就是从小到大。如果要使用逆序可以使用“orderby a descending”,正序是默认的,不过也可以加上ascending。

6.维恩图工具

Enumerable类提供了一些扩展方法,可以对两个(或多个)LINQ查询的数据进行合并(union)、比较(difference)、连接(concatenation)和交叉(intersection)。比如:

List<string> myMoney=new List<string>{"1元","2元","5元","10元"};
List<string> yourMoney=new List<string>{"10元","20元","50元","100元"};

var ourMoney=(from m in myMoney select m).Union(from y in yourMoney select y);

这样一来,我们的前就合并了哈……

7.移除重复

移除重复就像前面的反转一样:

    foreach (var i in alarmSubset.Reverse())
    {
        Console.WriteLine(i.ToString());
    }
    foreach (var i in alarmSubset.Distinct())
    {
        Console.WriteLine(i.ToString());
    }

8.聚合操作

前面的Count()就是聚合中的一个例子,此外还有Average()、Max()、Min()、Sum()等等。




感谢您的访问,希望对您有所帮助。

欢迎大家关注或收藏、评论或点赞。



为使本文得到斧正和提问,转载请注明出处:
http://blog.csdn.net/nomasp


时间: 2024-11-02 08:08:43

【LINQ技术】扩展特性和LINQ操作符的相关文章

什么是linq技术?

语言集成查询 (LINQ) 是一组技术的名称,这些技术建立在将查询功能直接集成到 C# 语言(以及 Visual Basic 和可能的任何其他 .NET 语言)的基础上. 借助于 LINQ,查询现在已是高级语言构造,就如同类.方法.事件等等. (1)什么是linq技术? 这个是在<ASP.NET高级程序设计第四版>第一章节中讲解asp.net版本中有关于该技术的起源背景,linq是asp.net 3.5 中跟AJAX一起在原来2.0版本上引入的一项新技术. 接下来是13章节中的一些知识点结合我

一起谈.NET技术,NHibernate3.0剖析:Query篇之NHibernate.Linq自定义扩展

系列引入 NHibernate3.0剖析系列分别从Configuration篇.Mapping篇.Query篇.Session策略篇.应用篇等方面全面揭示NHibernate3.0新特性和应用及其各种应用程序的集成,基于NHibernte3.0版本.如果你还不熟悉NHibernate,可以快速阅读NHibernate之旅系列文章导航系列入门,如果你已经在用NHibernate了,那么请跟上NHibernate3.0剖析系列吧. NHibernate专题:http://kb.cnblogs.com

Linq之延迟加载特性

目录 写在前面 系列文章 延迟加载 总结 写在前面 上篇文章介绍了linq中常见的几个关键字,并列举了几个例子,算是对linq如何使用有了初步了解.上篇文章中也提到了,能够使用linq的场合有一个要求:实现IEnumerable<T>泛型接口,或者类型兼容(可以通过Cast方法转换,比如ArrayList). 系列文章 Linq之Lambda表达式初步认识 Linq之Lambda进阶 Linq之隐式类型.自动属性.初始化器.匿名类 Linq之扩展方法 Linq之Expression初见 Lin

NHibernate3.0剖析:Query篇之NHibernate.Linq自定义扩展

系列引入 NHibernate3.0剖析系列分别从Configuration篇.Mapping篇.Query篇.Session策略篇.应用篇等方面全面揭示NHibernate3.0新特性和应用及其各种应用程序的集成,基于NHibernte3.0版本.如果你还不熟悉NHibernate,可以快速阅读NHibernate之旅系列文章导航系列入门,如果你已经在用NHibernate了,那么请跟上NHibernate3.0剖析系列吧. NHibernate专题:http://kb.cnblogs.com

Linq之扩展方法

目录 写在前面 系列文章 扩展方法 总结 写在前面 上篇文章介绍了隐式类型,自动属性,初始化器,匿名类的相关概念,及通过反编译的方式查看了编译器帮我们做了那些事.本篇文章将介绍扩展方法的知识点,及如何定义,如何使用的相关内容. 系列文章 Linq之Lambda表达式初步认识 Linq之Lambda进阶 Linq之隐式类型.自动属性.初始化器.匿名类 扩展方法 扩展方法使你能够向现有类型"添加"方法,而无需创建新的派生类型.重新编译或以其他方式修改原始类型. 扩展方法是一种特殊的静态方法

Linq入门——什么是linq &amp;amp; 扩展方法

 一,什么是Linq                      linq(language integrated Query):语言集成查询:      linq包含如下:                 对对象的查询,对数据库的查询,对XML的查询.       那么,没有linq前我们是怎样查询的?             先看一个例子:             现在我们要查询大于50的数,:          在没有linq之前,我们的代码时这样的:                   

linq的相关技术生成-求大神,帮忙把SQL转成LINQ,小弟第一次涉及LINQ需要参考谢谢了

问题描述 求大神,帮忙把SQL转成LINQ,小弟第一次涉及LINQ需要参考谢谢了 select * from ( select a.codea.gid from productonsale a productinformation b GeneralStandardCategory c where a.prodid=b.gid and b.StdCatID =c.gid and c.code in ('607''609') and a.ChlID='0DB3B832-3E16-E111-A1F0

.NET深入解析LINQ框架(一:LINQ优雅的前奏)

阅读目录: 1.LINQ简述 2.LINQ优雅前奏的音符 2.1.隐式类型 (由编辑器自动根据表达式推断出对象的最终类型) 2.2.对象初始化器 (简化了对象的创建及初始化的过程) 2.3.Lambda表达式 (对匿名方法的改进,加入了委托签名的类型推断并很好的与表达式树的结合) 2.4.扩展方法 (允许在不修改类型的内部代码的情况下为类型添加独立的行为) 2.5.匿名类型 (由对象初始化器推断得出的类型,该类型在编译后自动创建) 2.6.表达式目录树(用数据结构表示程序逻辑代码) 3.LINQ

Flink常见的关键技术与特性详解

Flink项目是大数据处理领域最近冉冉升起的一颗新星,其不同于其他大数据项目的诸多特性吸引了越来越多的人关注Flink项目.本文将深入分析Flink一些关键的技术与特性,希望能够帮助读者对Flink有更加深入的了解,对其他大数据系统的开发者也能有所裨益. 注:本文假设读者对MapReduce,Spark及Storm等大数据处理系统有基本了解,同时熟悉流处理与批处理的基本概念.36大数据(http://www.36dsj.com/) Flink简介 Flink的核心是一个流式的数据流执行引擎,其针