LINQ для начинающих

Мне нравится С#, мне нравится фреймворк, и я также люблю учиться как можно больше. Сегодня я начал читать статьи о LINQ в С#, и я не мог найти ничего хорошего для новичка, который никогда не работал с SQL в своей жизни.

Я нашел эту статью очень полезной, и я понял ее небольшие части, но я хотел бы получить больше примеров.

Прочитав пару раз, я попытался использовать 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); 
                }
            }
        }
    }

Я прошу две вещи:

  • Как бы выглядела функция с использованием максимально возможного Linq?
  • Веб-сайт/книга/статья о Linq, но, пожалуйста, обратите внимание, что я достойный новичок в sql/linq.

Заранее благодарю вас!

Ответ 1

Ну, одна вещь, которая сделает ваш образец более "LINQy" IEnumerable<string> для чтения строк из файла. Здесь несколько упрощенная версия моего класса LineReader из MiscUtil:

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);

Однако я предпочитаю читаемость исходного запроса:)

Ответ 2

101 Образцы LINQ, безусловно, хорошая коллекция примеров. Также LINQPad может быть хорошим способом играть с LINQ.

Ответ 7

Сначала я бы представил этот метод:

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);
        }
    }
}

Ответ 8

Что касается книг Linq, я бы порекомендовал:

ENhFS.jpg
(источник: ebookpdf.net)
http://www.diesel-ebooks.com/mas_assets/full/0321564189.jpg

Обе прекрасные книги, в которых подробно рассказывается о Linq.

Чтобы добавить еще одну вариацию к теме "как можно больше linq-как-возможно", вот мой взгляд:

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

Вы можете улучшить требования к памяти для этого решения, заменив "File.ReadAllLines" и "File.WriteAllLines" на Jon Skeet LineReader (и LineWriter в том же духе, взяв IEnumerable и записав каждый возвращенный элемент в выходной файл в качестве новой строки). Это преобразовало бы приведенное выше решение из "получить все строки в память как массив, отфильтровать их, создать другой массив в памяти для результата и записать этот результат в выходной файл, чтобы" читать "строки из входного файла одну за другой, и если это строка соответствует нашим критериям, немедленно запишите ее в выходной файл "(конвейерный подход).

Ответ 9

Я нашел эту статью чрезвычайно важной для понимания LINQ, которая основана на множестве новых конструкций, внедренных в .NET 3.0 и 3.5:

Я предупреждаю вас, что это долго читается, но если вы действительно хотите понять, что такое Linq, и я считаю, что это важно

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

Счастливое чтение

Ответ 10

Если бы я мог переписать функцию фильтра, используя 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)
        }
    }
}

Ответ 11

Чтобы ответить на первый вопрос, откровенно не слишком много причин использовать 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); 
        }
    }
}

Ответ 12

не может просто проверить, истинна ли 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 элемента.