65.9K
CodeProject 正在变化。 阅读更多。
Home

LINQ 入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (13投票s)

2010 年 5 月 29 日

CPOL

3分钟阅读

viewsIcon

88218

一篇阐述 LINQ 背后一些基本概念的文章。

引言

本文的目的是为可能尚未理解 LINQ 的某些个人介绍一些关于 LINQ 的基本知识。LINQ 统一了数据访问,无论数据源是什么,并允许混合来自不同来源的数据。 LINQ 意为“语言集成查询”。它允许进行查询和集合操作,类似于 SQL 语句为数据库提供的功能。然而,LINQ 通过对 C# 和 Visual Basic 等 .NET 语言的一组扩展,将查询直接集成到这些语言中。

在 LINQ 出现之前,开发人员在使用 C# 或 VB.NET 等通用语言编写的每个应用程序中,都必须处理不同的语言,如 SQL、XML 或 XPath,以及各种技术和 API,如 ADO.NET 或 System.Xml。毫无疑问,这存在几个缺点。 LINQ 将几个世界焊接在一起。它帮助我们避免了在从一个世界到另一个世界的道路上通常会遇到的障碍:使用 XML 与对象、混合关系数据与 XML,这些都是 LINQ 将简化的一些任务。 LINQ 的一个关键方面是,它被设计为用于任何类型的对象或数据源,并为此提供一致的编程模型。语法和概念在其所有用途中都是相同的:一旦您学会了如何使用 LINQ 处理数组或集合,您也就知道了利用 LINQ 处理数据库或 XML 文件所需的大部分概念。 LINQ 的另一个重要方面是,当您使用它时,您工作在一个强类型世界中。检查这段基本代码,看看它是否显示与数据源的任何链接

using System;
using System.Linq;
public sealed class Program {
static double Square(double n)
{
  Console.WriteLine("Computing Square("+n+")...");
  return Math.Pow(n, 2);
}

public static void Main()
{
  int[] numbers = {1, 2, 3};

  var query =
      from n in numbers
      select Square(n);

  foreach (var n in query)
      Console.WriteLine(n);
  }
}
输出
Computing Square(1)...
1
Computing Square(2)...
4
Computing Square(3)...
9

该代码声明了一个方法 Square,然后声明一个隐式类型的局部变量来对一个由三个整数组成的数组(或序列)执行该操作。 select 方法发出一个序列,其中每个输入元素都在给定的 lambda 表达式中转换。每个元素的迭代使操作能够在每个元素上执行。事实上,枚举器的基本思想是一种其唯一目的是遍历和读取另一个集合内容的类型。枚举器不提供写入功能。这种类型可以被视为一个光标,它一次一个地遍历集合中的每个单独元素。 IEnumerable<t> 表示其内容可以被枚举的类型,而 IEnumerator<t> 是负责执行实际枚举的类型。 LINQ 中的基本数据单位是序列和元素。序列是任何实现泛型 IEnumerable 接口的对象,而元素是序列中的每个项目。这是一个基本代码示例

using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
    public static void Main()
    {
        string[] names = { "Tom", "Mitch", "Steve" };
        IEnumerable <string> filteredNames = 
           System.Linq.Enumerable.Where (names, n => n.Length >= 4);
        foreach ( string n in filteredNames)
            sConsole.Write(n + "|");
    }
}

这是输出

Mitch
Steve

Lambda 表达式:链接查询运算符

前面的示例不太现实,因为它展示了两个基本的 lambda 查询,每个查询都包含一个查询运算符。要构建更复杂的查询,您需要链接运算符

using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
    public static void Main()
    {
        string[] names = { "Tom", "Dick", "Harry", 
                 "Mary", "Jay" };

        IEnumerable<string> query = names
            .Where   (n => n.Contains ("a"))
            .OrderBy (n => n.Length)
            .Select  (n => n.ToUpper());

        foreach (string name in query)
            Console.Write(name + "|");
    }
}

// end of program
// The same query constructed progressively:

IEnumerable<string> filtered   = names.Where      (n => n.Contains ("a"));
IEnumerable<string> sorted     = filtered.OrderBy (n => n.Length);
IEnumerable<string> finalQuery = sorted.Select    (n => n.ToUpper());

这是一个更复杂的查询,它通过使用关键字 "var" 来使用隐式类型的局部变量

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
static class LanguageFeatures
{
    class ProcessData
    {
        public Int32 Id { get; set; }
        public Int64 Memory { get; set; }
        public String Name { get; set; }
    }

    static void DisplayProcesses(Func<Process, Boolean> match)
    {
        // implicitly-typed local variables
        var processes = new List<ProcessData>();
        foreach (var process in Process.GetProcesses())
        {
            if (match(process))
            {
                // object initializers
                processes.Add(new ProcessData
                {
                    Id = process.Id,
                    Name = process.ProcessName,
                    Memory = process.WorkingSet64
                });
            }
        }

        // extension methods
        Console.WriteLine("Total memory: {0} MB",
                processes.TotalMemory() / 1024 / 1024);
        var top2Memory =
          processes
            .OrderByDescending(process => process.Memory)
            .Take(2)
            .Sum(process => process.Memory) / 1024 / 1024;
        Console.WriteLine(
          "Memory consumed by the two most hungry processes: {0} MB",
          top2Memory);

        // anonymous types
        var results = new
        {
            TotalMemory = processes.TotalMemory() / 1024 / 1024,
            Top2Memory = top2Memory,
            Processes = processes
        };
        ObjectDumper.Write(results, 1);

        ObjectDumper.Write(processes);
    }

    static Int64 TotalMemory(this IEnumerable<ProcessData> processes)
    {
        Int64 result = 0;

        foreach (var process in processes)
            result += process.Memory;

        return result;
    }
    static void Main()
    {
        // lambda expressions
        DisplayProcesses(process => process.WorkingSet64 >= 20 * 1024 * 1024);
    }
}

如果您检查此代码,您将看到 "ObjectDumper" 未定义,但被引用。这意味着我们有一个 DLL 引用文件需要编译

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

public class ObjectDumper {

    public static void Write(object element)
    {
        Write(element, 0);
    }

    public static void Write(object element, int depth)
    {
        Write(element, depth, Console.Out);
    }

    public static void Write(object element, int depth, TextWriter log)
    {
        ObjectDumper dumper = new ObjectDumper(depth);
        dumper.writer = log;
        dumper.WriteObject(null, element);
    }

    TextWriter writer;
    int pos;
    int level;
    int depth;

    private ObjectDumper(int depth)
    {
        this.depth = depth;
    }

    private void Write(string s)
    {
        if (s != null) {
            writer.Write(s);
            pos += s.Length;
        }
    }

    private void WriteIndent()
    {
        for (int i = 0; i < level; i++) writer.Write("  ");
    }

    private void WriteLine()
    {
        writer.WriteLine();
        pos = 0;
    }

    private void WriteTab()
    {
        Write("  ");
        while (pos % 8 != 0) Write(" ");
    }

    private void WriteObject(string prefix, object element)
    {
        if (element == null || element is ValueType || element is string) {
            WriteIndent();
            Write(prefix);
            WriteValue(element);
            WriteLine();
        }
        else {
            IEnumerable enumerableElement = element as IEnumerable;
            if (enumerableElement != null) {
                foreach (object item in enumerableElement) {
                    if (item is IEnumerable && !(item is string)) {
                        WriteIndent();
                        Write(prefix);
                        Write("...");
                        WriteLine();
                        if (level < depth) {
                            level++;
                            WriteObject(prefix, item);
                            level--;
                        }
                    }
                    else {
                        WriteObject(prefix, item);
                    }
                }
            }
            else {
                MemberInfo[] members = element.GetType().GetMembers(
                             BindingFlags.Public | BindingFlags.Instance);
                WriteIndent();
                Write(prefix);
                bool propWritten = false;
                foreach (MemberInfo m in members) {
                    FieldInfo f = m as FieldInfo;
                    PropertyInfo p = m as PropertyInfo;
                    if (f != null || p != null) {
                        if (propWritten) {
                            WriteTab();
                        }
                        else {
                            propWritten = true;
                        }
                        Write(m.Name);
                        Write("=");
                        Type t = f != null ? f.FieldType : p.PropertyType;
                        if (t.IsValueType || t == typeof(string)) {
                            WriteValue(f != null ? f.GetValue(element) : 
                                       p.GetValue(element, null));
                        }
                        else {
                            if (typeof(IEnumerable).IsAssignableFrom(t)) {
                                Write("...");
                            }
                            else {
                                Write("{ }");
                            }
                        }
                    }
                }
                if (propWritten) WriteLine();
                if (level < depth) {
                    foreach (MemberInfo m in members) {
                        FieldInfo f = m as FieldInfo;
                        PropertyInfo p = m as PropertyInfo;
                        if (f != null || p != null) {
                            Type t = f != null ? f.FieldType : p.PropertyType;
                            if (!(t.IsValueType || t == typeof(string))) {
                                object value = f != null ? 
                                       f.GetValue(element) : p.GetValue(element, null);
                                if (value != null) {
                                    level++;
                                    WriteObject(m.Name + ": ", value);
                                    level--;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void WriteValue(object o)
    {
        if (o == null) {
            Write("null");
        }
        else if (o is DateTime) {
            Write(((DateTime)o).ToShortDateString());
        }
        else if (o is ValueType || o is string) {
            Write(o.ToString());
        }
        else if (o is IEnumerable) {
            Write("...");
        }
        else {
            Write("{ }");
        }
    }
}

现在,我们使用命令行上的 /target:library 选项将 ObjectDumper.cs 文件编译成 DLL,或者在 VS 2010 中将其编译为类文件。请注意,如果您使用的是 VS 2010,请确保转到项目的属性并确保 .NET 平台是 4.0。现在,我们使用对 ObjectDumper.dll 的引用来编译上面的文件 MyProgram.cscsc.exe /r:ObjectDumper.dll MyProgram.cs。这是输出

C:\Windows\MICROS~1.NET\FRAMEW~1\V40~1.303>myprogram
Total memory: 968 MB
Memory consumed by the two most hungry processes: 314 MB
TotalMemory=968         Top2Memory=314  Processes=...
  Processes: Id=3244      Memory=65527808         Name=sqlservr
  Processes: Id=5320      Memory=23556096         Name=sqlservr
  Processes: Id=3320      Memory=37498880         Name=DkService
  Processes: Id=952       Memory=47443968         Name=svchost
  Processes: Id=5272      Memory=167903232        Name=WINWORD
  Processes: Id=1108      Memory=68866048         Name=svchost
  Processes: Id=1096      Memory=90230784         Name=svchost
  Processes: Id=500       Memory=120848384        Name=AcroRd32
  Processes: Id=2856      Memory=75415552         Name=explorer
  Processes: Id=1672      Memory=71299072         Name=digitaleditions
  Processes: Id=4348      Memory=162045952        Name=LINQPad
  Processes: Id=2576      Memory=35442688         Name=Babylon
  Processes: Id=2172      Memory=49131520         Name=SearchIndexer
Id=3244         Memory=65527808         Name=sqlservr
Id=5320         Memory=23556096         Name=sqlservr
Id=3320         Memory=37498880         Name=DkService
Id=952  Memory=47443968         Name=svchost
Id=5272         Memory=167903232        Name=WINWORD
Id=1108         Memory=68866048         Name=svchost
Id=1096         Memory=90230784         Name=svchost
Id=500  Memory=120848384        Name=AcroRd32
Id=2856         Memory=75415552         Name=explorer
Id=1672         Memory=71299072         Name=digitaleditions
Id=4348         Memory=162045952        Name=LINQPad
Id=2576         Memory=35442688         Name=Babylon
Id=2172         Memory=49131520         Name=SearchIndexer

3.jpg

5.jpg

© . All rights reserved.