[.net 面向对象编程基础] (19) LINQ基础

时间:2021-08-26 23:08:40

[.net 面向对象编程基础] (19)  LINQ基础

上两节我们介绍了.net的数组、集合和泛型。我们说到,数组是从以前编程语言延伸过来的一种引用类型,采用事先定义长度分配存储区域的方式。而集合是.Net 版本初期的用于解决数据集检索方便而设计的,它比数组的优势除了检索方便之外,还可以在使用过程中自动分配存储区域,不需要事先定义大小。但是集合存在类型不安全以及频繁装箱、拆箱操作带来的性能问题。泛型是.net 2.0以后为了解决集合的缺陷而设计的,采用实例调用阶段再声明类型的方法,即解决了安全问题,也解决了效率问题。

随着.net 3.5以后版本的发展,微软设计了LINQ,让我们检索更加方便,开发更加高效了。

1.LINQ概念

LINQ,语言集成查询(Language Integrated Query)是一组用于c#和Visual Basic语言的扩展。它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作内存数据。

以上是来自百度百科的定义。

通过英文名描述,我们可以看出,它就是一种让开发者像查询数据库一样,去检索内存数据的方案。

2.LINQ学习之前需要掌握以下知识点

看着有点多,但是都是学好LINQ的基础,我们说了LINQ就是像SQL语句一样操作内存数据,那么学习SQL要掌握表,字段,视图的基础概念,LINQ也一样。

2.1 隐式类型

之前我们定义变量时,需要指定一个类型,foreach遍历时,也要指定类型。隐式类型的出现我们不再需要做这个工作了,而使用var 定义就可以了,这点更像弱类型语言比如javascipt。但是.NET并不是弱类型语言,隐式类型的定义写法如下:

var i=;
var b=”xyz”;
var obj=new MyObj();

以上隐式类型等效于

int i=;
string b=”xyz”;
MyObj obj=new MyObj();

关于隐式类型的几点说明:

A.有了隐式类型,可以编码中使用var定义任意类型变量

B.隐式类型并不会影响程序性能,.NET会在编译时帮我们转换成具体的数据类型,因此我们必须声明隐式类型时赋值,不然.NET 不能判断具体转成什么类型而报错。

C.隐式类型var 使我们开发节约了不少时间。定义一个变量时,类型需要输两次,var 一下就搞定了。在foreach遍历的时候也可 以使用var来书写循环变量的类型。

2.2 匿名类型

我们new一个对象,它里面的元素,类型可以不申明,而使用匿名方式。如下:

//匿名类型
var obj =new { str = "abc", Guid.Empty,myArray = new int[] { , , } };
Console.WriteLine(obj.str);
Console.WriteLine(obj.Empty);
Console.WriteLine(obj.myArray[]);
Console.ReadLine();

运行结果如下:

[.net 面向对象编程基础] (19)  LINQ基础

我们看到new 一个对象后,自动为对象定义了属性,并为这些属性赋值,当把一个对象的属性拷贝到匿名对象中时,可以不用显示的指定属性的名字,这时原始属性的名字会被“拷贝”到匿名对象中.如下图:

[.net 面向对象编程基础] (19)  LINQ基础

自动创建了obj.Empty属性名。
如果你监视变量obj,你会发现,obj的类型是Anonymous Type类型的。
不要试图在创建匿名对象的方法外面去访问对象的属性!
这个特性在网站开发中,序列化和反序列化JSON对象时很有用。

2.3 自动属性

记得在“重构”一节中,说了属性的重构,只需要字段名上使用vs.net提供的“封装字段”,就为我们生成了相对应的属性,如下图:

[.net 面向对象编程基础] (19)  LINQ基础

(图1)

[.net 面向对象编程基础] (19)  LINQ基础
(图2)
[.net 面向对象编程基础] (19)  LINQ基础

(图3)

自.net 3.0以后,我们代码可以写的更简单了,代码如下:

//自动属性
class Flower {
public string Leaf{get;set;}
public string Name{get;set;}
}

重构当然只是.net的辅助功能,对于自动属性小伙伴们肯定会担心这样写的效率,这点完全可以放心,跟var 隐式类型一样,.net在编译的时候,会帮我们写完整的,没有任何性能问题。

2.4 初始化器

对于一个对象的初始化,假如有以下两个对象:

//初始化器
class Flower {
public string Leaf{get;set;}
public string Name{get;set;}
} class Bear
{
public Bear(string name){}
public string Name { get; set; }
public double Weight { get; set; } }

在3.0以前的版中,我们一般类似于如下写法:

//初始化器
Flower flower = new Flower();
flower.Leaf = "叶子";
flower.Name = "棉花";

在3.0以后的版本中,我们可以这样写:

//.net 3.0以后对象初始化可以如下写法
Flower flower2 = new Flower() { Name = "桃花", Leaf = "桃叶" };
//构造函数带参数对象初始化
Bear bear = new Bear("北极熊"){ Name="熊熊", Weight=};
//集合初始化
var array = new List<char>() { 'a', 'b','c', 'd', 'e','f' };

我们可以看出,在写法上简洁好多了。特别是对泛型集合的赋值,相当简洁明了。

2.5 匿名方法

说到匿名方法,就是说在委托一个方法的时候,可以把方法写在委托声明的时候,简单说就是不用把方法单独写出来。在说这个之前,我们本来是要介绍一下委托的,但是下一节,我们会重点说明委托,在这里只需要了解匿名方法就可以了,下面看一个例子:

 // 委托函数
Func<int, int, string> func1 = Adds;
// 匿名方法
Func<int, int, string> func2 =
delegate(int a, int b)
{
return a+"+"+b+"等于几?" + Environment.NewLine +(a+b).ToString();
};
// Lambda表达式
Func<int, int, string> func3 =
(a, b) => { return a + "+" + b + "等于几?" + Environment.NewLine + (a + b).ToString(); }; // 调用Func委托
string sum = func2(, ); Console.WriteLine(sum);
Console.ReadLine();
//委托方法
static string Adds(int a, int b)
{
return a + "+" + b + "等于几?" + Environment.NewLine + (a + b).ToString();
}

通过使用匿名方法,可以访问上下文中的变量,在方法本身不是很长的情况下,轻量级的写法,非常实用。

这点在下一节委托中具体说明,小伙伴在这里没看明白也没关系。

2.6 扩展方法

1) 扩展方法声明在静态类中,定义为一个静态方法,其第一个参数需要使用this关键字标识,指示它所扩展的类型。

2) 扩展方法可以将方法写入最初没有提供该方法的类中。还可以把方法添加到实现某个接口的任何类中,这样多个类就可以使用相同的实现代码。(LINQ中,System.Linq.Queryable.cs和System.Linq.Enumerable.cs 正是对接口添加扩展方法)

3) 扩展方法虽定义为一个静态方法,但其调用时不必提供定义静态方法的类名,只需引入对应的命名空间,访问方式同实例方法。

4) 扩展方法不能访问它所扩展的类型的私有成员。

例子:

public static IEnumerable<TSource> MyWhere<TSource>(
this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
foreach (TSource item in source)
{
if (predicate(item))
yield return item;
}
}

我们再看一下扩展方法的厉害的地方,要给一个类型增加行为,不一定要使用继承的方法实现,还可以这样写:

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

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

(1)先决条件

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

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

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

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

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

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

(2)注意事项

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

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

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

2.7 迭代器

(1)使用

我们每次针对集合类型编写foreach代码块,都是在使用迭代器

这些集合类型都实现了IEnumerable接口

都有一个GetEnumerator方法

但对于数组类型就不是这样

编译器把针对数组类型的foreach代码块

替换成了for代码块。

来看看List的类型签名: 

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable

IEnumerable接口,只定义了一个方法就是:   

IEnumerator<T> GetEnumerator();

(2)迭代器的优点

 假设我们需要遍历一个庞大的集合

 只要集合中的某一个元素满足条件

 就完成了任务

 你认为需要把这个庞大的集合全部加载到内存中来吗?

 当然不用(C#3.0之后就不用了)!

 来看看这段代码:  

static IEnumerable<int> GetIterator()
{
Console.WriteLine("迭代器返回了1");
yield return ;
Console.WriteLine("迭代器返回了2");
yield return ;
Console.WriteLine("迭代器返回了3");
yield return ;
}
foreach (var i in GetIterator())
{
if (i == )
{
break;
}
Console.WriteLine(i);
}
Console.ReadKey();

输出结果为:    

迭代器返回了1

迭代器返回了2

大家可以看到:

当迭代器返回2之后,foreach就退出了

并没有输出“迭代器返回了3”

也就是说下面的工作没有做。

(3)yield 关键字

MSDN中的解释如下:

在迭代器块中用于向枚举数对象提供值或发出迭代结束信号。

也就是说,我们可以在生成迭代器的时候,来确定什么时候终结迭代逻辑

上面的代码可以改成如下形式:  

static IEnumerable<int> GetIterator()
{
Console.WriteLine("迭代器返回了1");
yield return ;
Console.WriteLine("迭代器返回了2");
yield break;
Console.WriteLine("迭代器返回了3");
yield return ;
}

(4)注意事项

<1>做foreach循环时多考虑线程安全性      

在foreach时不要试图对被遍历的集合进行remove和add等操作

任何集合,即使被标记为线程安全的,在foreach的时候,增加项和移除项的操作都会导致异常

<2>IEnumerable接口是LINQ特性的核心接口

只有实现了IEnumerable接口的集合

才能执行相关的LINQ操作,比如select,where等

关于LINQ的具体操作,下一节继承。

2.8 Lambda表达式

Lambda表达式只是用更简单的方式来书写匿名方法,从而彻底简化.NET委托类型的使用。

Lambda表达式在C#中的写法是“arg-list => expr-body”,“=>”符号左边为表达式的参数列表,右边则是表达式体(body)。参数列表可以包含0到多个参数,参数之间使用逗号分割。

通过上面匿名方法的例子,我们可以看到,下面两段代码是等效的:

// 匿名方法
Func<int, int, string> func2 =
delegate(int a, int b)
{
return a+"+"+b+"等于几?" + Environment.NewLine +(a+b).ToString();
};
// Lambda表达式
Func<int, int, string> func3 =
(a, b) => { return a + "+" + b + "等于几?" + Environment.NewLine + (a + b).ToString(); };

Lambda表达式基于数学中的λ(希腊第11个字母)演算得名,而“Lambda 表达式”(lambda expression)是指用一种简单的方法书写匿名方法。

3.要点:

A.LINQ,语言集成查询(Language Integrated Query)是一种语言扩展,让我们像查询数据库一样去操作内存数据(集合等).

B.匿名类型:使用new声明匿名类型时,不需指明类型,.NET 3.0以后版本,可以自动完成类型指定和指定属性名称

C.自动属性:.net 3.0 以后版本中,我们定义属性,只需要简单书写get;set;就可以了,.net在编译阶段会帮助我们完成书写,不会存在性能问题。

D.初始化器:在3.0以后版本中,可以更加简单的书写对象初始化,特别是对泛型集合的赋值,相当简洁明了。

E.匿名方法:在委托方法时,无需写明方法名称.使用匿名方法可以访问上下文中的变量,在方法代码较少的情况下,轻量级实现委托,非常实用。

F.扩展方法:可以在不使用继承的方式给一个类型增加行为

G.迭代器:在遍历一个庞大集合时,不需要将它全部加载于是内存中,只要满足条件就可以返回了。

H.Lambda表达式:Lambda表达式基于数学中的λ(希腊第11个字母)演算得名,而“Lambda 表达式”(lambda expression)是指用一种简单的方法书写匿名方法

备注:本文参考了博友们的一些文章,数目较多,不一一列举,在此表示感谢。

对于LINQ的使用我们下一节详细说明。

==============================================================================================

返回目录

 <如果对你有帮助,记得点一下推荐哦,如有有不明白或错误之处,请多交流>

<转载声明:技术需要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>

.NET 技术交流群:467189533    [.net 面向对象编程基础] (19)  LINQ基础

==============================================================================================

[.net 面向对象编程基础] (19) LINQ基础的更多相关文章

  1. PHP面向对象编程学习之对象基础

    php虽然是一门学习起来非常简单的语言,但是这门语言也包含了对面向对象编程的支持.尤其是随着php5的发布,php对面向对象的支持有了很大的进步.最近学习了一下php的面向对象编程,不禁感慨,面向对象 ...

  2. PHP面向对象编程(1)基础

    一.面向对象OOP(Oriented Object Programming) 面向过程的编程 将要实现的功能描述为一个从一开始到结束的连续的“步骤(过程)”. 一次逐步完成这些步骤.如果步骤比较大,又 ...

  3. 面向对象编程(OOP)基础知识(一)

    Java是一个支持并发.基于类和面向对象的计算机编程语言. 下面列出了面向对象软件开发的优点: 1.代码开发模块化,更易维护和修改. 2.代码复用. 3.增强代码的可靠性和灵活性. 4.增加代码的可理 ...

  4. JavaScript面向对象编程(1)-- 基础

    自从有了Ajax这个概念,JavaScript作为Ajax的利器,其作用一路飙升.JavaScript最基本的使用,以及语法.浏览器对象等等东东在这里就不累赘了.把主要篇幅放在如何实现JavaScri ...

  5. java面向对象编程——第二章 java基础语法

    第二章 java基础语法 1. java关键字 abstract boolean break byte case catch char class const continue default do ...

  6. 浅谈PHP面向对象编程(二、基础知识)

    和一些面向对象的语言有所不同,PHP并不是一种纯面向对象的语言,包PIP它支持面向对象的程序设计,并可以用于开发大型的商业程序.因此学好面向对象输程对PHP程序员来说也是至关重要的.本章并针对面向对象 ...

  7. Web前端基础&lpar;19&rpar;&colon;jQuery基础&lpar;六&rpar;

    1. ajax 1.1 什么是ajax AJAX = 异步的javascript和XML(Asynchronous Javascript and XML) 简言之,在不重载整个网页的情况下,AJAX通 ...

  8. &lbrack;&period;net 面向对象编程基础&rsqb; &lpar;1&rpar; 开篇

    [.net 面向对象编程基础] (1)开篇 使用.net进行面向对象编程也有好长一段时间了,整天都忙于赶项目,完成项目任务之中.最近偶有闲暇,看了项目组中的同学写的代码,感慨颇深.感觉除了定义个类,就 ...

  9. 深入解读JavaScript面向对象编程实践

    面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式,主要包括模块化.多态.和封装几种技术.对JavaScript而言,其核心是支持面向对象的,同时它也提供了强大灵活的基于原型的面向对象编程能力 ...

随机推荐

  1. JavaScript把项目本地的图片或者图片的绝对路径转为base64字符串、blob对象在上传

    主题: JavaScript把项目本地的图片或者图片的绝对路径转为base64字符串.blob对象在上传. 用处: 从本地选择图片上传,如项目规定只能选择本项目文件夹下的图像上传为头像等. 主要思想: ...

  2. spring mvc学习笔记一:hello world

    下面是创建springmvc简单案例的步骤: 项目的目录结构如下: 此案例使用maven构建. 第一步:创建动态web工程,然后项目->configure->convert to mave ...

  3. www&period;97top10&period;com--做最好的技术交流网站

    www.97top10.com--做最好的技术交流网站

  4. IOS8修改状态栏颜色

    IOS8修改状态栏颜色 http://blog.csdn.net/suelu/article/details/43701913 使用了storyboard,直接view controller里面设置s ...

  5. &period;NET连接SAP系统专题:SAP中新建可远程调用的RFC(二)

    何谓RFC,就是一个Function,可以被非SAP系统调用,比如VB,C#,Java等.如果我们在RFC中INCLUDE了相关的业务逻辑,那么我们就可以完全操控SAP中的业务数据了.就像在TTE里, ...

  6. Git学习之添加远程仓库

    好久没有写过博客了,只因人生世事无常! 前言:说实话,早就听说了Git这个代码管理工具的NB之处,却一直没有时间好好学习下.现在终于有时间学习一下这个伟大的工具,在此写下在学习过程中遇到的问题! 推荐 ...

  7. 2018-2019-1 20189210 《LInux内核原理与分析》第六周作业

    系统调用实验(下): 将第四章的两个实验集成到MenuOS系统中,将其作为MenuOS系统的两个命令,新版本的menu中已经把两个系统调用添加进去了,只需重新克隆一个新版本的menu. 使用make ...

  8. flexible&period;js 移动端自适应方案

    一,flexible.js 的使用方式: github地址:https://github.com/amfe/lib-flexible 官方文档地址:https://github.com/amfe/ar ...

  9. idea Unable to open debugger port &lpar;127&period;0&period;0&period;1&colon;58006&rpar; Address already in use&colon; JVM&lowbar;Bind 的解决办法

    报错说端口58006 被占用了,于是去修改端口 重新dubug 发现换个端口号还是不行,同样的错误.有时候你把idea关闭重新打开依旧不起作用.最暴力的办法就是重启电脑... 问题解决: 查看使用中的 ...

  10. Java课程总结

    预备作业一 简要内容:我期望的师生关系 预备作业二 简要内容:学习基础和C语言基础调查 预备作业三 简要内容:Linux安装及学习 第一周作业 简要内容:Java入门 第二周作业 简要内容:学习基本数 ...