LINQ适合初学者

时间:2009-05-21 15:21:48

标签: c# linq

我喜欢C#,我喜欢这个框架,我也喜欢尽可能多地学习。今天我开始在C#中阅读有关LINQ的文章,我找不到任何对他以前从未使用SQL的初学者有用的东西。

我发现this文章非常有用,我理解它的一小部分内容,但我想了解更多示例。

在阅读了几次之后,我尝试在我的函数中使用LINQ,但是我失败了。

    private void Filter(string filename)
    {
        using (TextWriter writer = File.CreateText(Application.StartupPath + "\\temp\\test.txt"))
        {
            using(TextReader reader = File.OpenText(filename))
            {
                string line;
                while((line = reader.ReadLine()) != null)
                {
                    string[] items = line.Split('\t');
                    int myInteger = int.Parse(items[1]);
                    if (myInteger == 24809) writer.WriteLine(line); 
                }
            }
        }
    }

这就是我所做的,但没有奏效,结果总是错误的。

    private void Filter(string filename)
    {
        using (TextWriter writer = File.CreateText(Application.StartupPath + "\\temp\\test.txt"))
        {
            using(TextReader reader = File.OpenText(filename))
            {
                string line;
                while((line = reader.ReadLine()) != null)
                {
                    string[] items = line.Split('\t');
                    var Linqi = from item in items
                                where int.Parse(items[1]) == 24809
                                select true;
                    if (Linqi == true) writer.WriteLine(line); 
                }
            }
        }
    }

我要求两件事:

  1. 该函数如何使用尽可能多的Linq?
  2. 关于Linq的网站/书籍/文章,但请注意我在sql / linq中是个不错的初学者。
  3. 提前谢谢!

12 个答案:

答案 0 :(得分:19)

有一件事可以使你的样本更“LINQy”是一个IEnumerable<string>用于从文件中读取行。以下是来自MiscUtilLineReader课程的简化版本:

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

public sealed class LineReader : IEnumerable<string>
{
    readonly Func<TextReader> dataSource;

    public LineReader(string filename)
        : this(() => File.OpenText(filename))
    {
    }

    public LineReader(Func<TextReader> dataSource)
    {
        this.dataSource = dataSource;
    }

    public IEnumerator<string> GetEnumerator()
    {
        using (TextReader reader = dataSource())
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                yield return line;
            }
        }
    }


    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

现在你可以使用它:

    var query = from line in new LineReader(filename)
                let items = line.Split('\t')
                let myInteger int.Parse(items[1]);
                where myInteger == 24809
                select line;

    using (TextWriter writer = File.CreateText(Application.StartupPath 
                                               + "\\temp\\test.txt"))
    {
        foreach (string line in query)
        {
            writer.WriteLine(line);
        }
    }

请注意,没有let子句可能会更有效:

    var query = from line in new LineReader(filename)
                where int.Parse(line.Split('\t')[1]) == 24809
                select line;

此时你可以用“点符号”合理地完成所有操作:

    var query = new LineReader(filename)
                        .Where(line => int.Parse(line.Split('\t')[1]) == 24809);

但是,我更喜欢原始查询的可读性:)

答案 1 :(得分:6)

101 LINQ Samples肯定是一个很好的例子集合。同样LINQPad可能是玩LINQ的好方法。

答案 2 :(得分:5)

以网站为起点,您可以尝试Hooked on LINQ

修改
原始网站现在似乎已经死亡(域名出售)。

以下是上一版本的互联网档案:https://web.archive.org/web/20140823041217/http://www.hookedonlinq.com/

答案 3 :(得分:3)

如果你正在读书,我发现LINQ in action Manning Publications是一个很好的起点。

答案 4 :(得分:1)

答案 5 :(得分:1)

答案 6 :(得分:1)

首先,我将介绍这种方法:

private IEnumerable<string> ReadLines(StreamReader reader)
{
    while(!reader.EndOfStream)
    {
        yield return reader.ReadLine();
    }
}

然后,我会重构使用它的主要方法。我将两个using语句放在同一个块之上,并添加了范围检查以确保items[1]不会失败:

private void Filter(string fileName)
{
    using(var writer = File.CreateText(Application.StartupPath + "\\temp\\test.txt"))
    using(var reader = File.OpenText(filename))
    {
        var myIntegers =
            from line in ReadLines(reader)
            let items = line.Split('\t')
            where items.Length > 1
            let myInteger = Int32.Parse(items[1])
            where myInteger == 24809
            select myInteger;

        foreach(var myInteger in myIntegers)
        {
            writer.WriteLine(myInteger);
        }
    }
}

答案 7 :(得分:1)

对于Linq书籍,我建议:

http://www.ebookpdf.net/screen/cover2/51n4fa2xxvl_083.jpg http://www.diesel-ebooks.com/mas_assets/full/0321564189.jpg

两本都是出色的书籍,详细介绍了Linq。

要为as-much-linq-as-possible主题添加另一个变体,这是我的看法:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace LinqDemo
{
    class Program
    {
        static void Main()
        {
            var baseDir = AppDomain.CurrentDomain.BaseDirectory;
            File.WriteAllLines(
                Path.Combine(baseDir, "out.txt"),
                File.ReadAllLines(Path.Combine(baseDir, "in.txt"))
                    .Select(line => new KeyValuePair<string, string[]>(line, line.Split(','))) // split each line into columns, also carry the original line forward
                    .Where(info => info.Value.Length > 1) // filter out lines that don't have 2nd column
                    .Select(info => new KeyValuePair<string, int>(info.Key, int.Parse(info.Value[1]))) // convert 2nd column to int, still carrying the original line forward
                    .Where(info => info.Value == 24809) // apply the filtering criteria
                    .Select(info => info.Key) // restore original lines
                    .ToArray());
        }
    }
}

请注意,我将制表符分隔列更改为以逗号分隔的列(在我的编辑器中更容易将制表符转换为空格;-))。对输入文件运行此程序时:

A1,2
B,24809,C
C

E
G,24809

输出将是:

B,24809,C
G,24809

你可以通过用Jon Skeet的LineReader替换“File.ReadAllLines”和“File.WriteAllLines”来改善这个解决方案的内存需求(和类似的LineWriter一样,采用IEnumerable并将每个返回的项目作为一个新的输出文件写入线)。这将改变上面的解决方案“将所有行作为数组放入内存,过滤掉它们,在内存中创建另一个数组以获得结果并将此结果写入输出文件”以“逐个”读取输入文件中的行,如果是这样的话line符合我们的标准,立即将其写入输出文件“(管道方法)。

答案 8 :(得分:1)

我发现这篇文章对于理解LINQ非常重要,LINQ基于.NET 3.0中引入的许多新结构。 3.5:

我会警告你,这是一个很长的阅读,但如果你真的想了解Linq是什么,我相信它是必不可少的

http://blogs.msdn.com/ericwhite/pages/FP-Tutorial.aspx

快乐阅读

答案 9 :(得分:0)

如果我在可能的情况下使用LINQ重写过滤函数,它看起来像这样:

private void Filter(string filename)
{
    using (TextWriter writer = File.CreateText(Application.StartupPath + "\\temp\\test.txt"))
    {
        var lines = File.ReadAllLines(filename);
        var matches = from line in lines
                      let items = line.Split('\t')
                      let myInteger = int.Parse(items[1]);
                      where myInteger == 24809
                      select line;

        foreach (var match in matches)
        {
            writer.WriteLine(line)
        }
    }
}

答案 10 :(得分:0)

回答第一个问题,坦率地说,除了作为练习外,没有太多理由以上述功能的方式使用LINQ。事实上,它可能只是让函数更难阅读。

LINQ在集合上运行比单个元素更有用,我会以这种方式使用它。所以,这是我尝试在函数中使用尽可能多的LINQ (没有提到效率,我建议将整个文件读入内存,就像这样) :

private void Filter(string filename)
{
    using (TextWriter writer = File.CreateText(Application.StartupPath + "\\temp\\test.txt"))
    {
        using(TextReader reader = File.OpenText(filename))
        {
            List<string> lines;
            string line;
            while((line = reader.ReadLine()) != null)
                lines.Add(line);

            var query = from l in lines
                        let splitLine = l.Split('\t')
                        where int.Parse(splitLine.Skip(1).First()) == 24809
                        select l;

            foreach(var l in query)               
                writer.WriteLine(l); 
        }
    }
}

答案 11 :(得分:-1)

不能只检查Linqi是否属实...... Linqi是IEnumerable<bool>(在这种情况下)所以必须检查Linqi.First() == true

这是一个小例子:

string[] items = { "12121", "2222", "24809", "23445", "24809" };

                        var Linqi = from item in items
                                    where Convert.ToInt32(item) == 24809
                                    select true;
                        if (Linqi.First() == true) Console.WriteLine("Got a true");

您也可以迭代Linqi,在我的例子中,该集合中有2个项目。