2009-05-21 20 views
10

Me encanta C#, me encanta el framework, y también me encanta aprender tanto como sea posible. Hoy comencé a leer artículos sobre LINQ en C# y no pude encontrar nada bueno para un principiante que nunca trabajó con SQL en su vida.LINQ para principiantes

Encontré this artículo muy útil y entendí pequeñas partes de él, pero me gustaría obtener más ejemplos.

Después de leerlo un par de veces, traté de usar LINQ en una función mía, pero fallé.

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

Esto es lo que hice y no funcionó, el resultado siempre fue falso.

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

estoy pidiendo dos cosas:

  1. ¿Cómo la función de parecerse a utilizar la mayor cantidad posible de LINQ?
  2. Un sitio web/libro/artículo sobre Linq, pero tenga en cuenta que soy un principiante decente en sql/linq.

Gracias de antemano!

+0

Sólo una nota. Su "where int.Parse (items [1]) == 24809" está mirando al segundo caracter en cada una de las columnas para cada línea. –

Respuesta

19

Bueno, una Lo que haría que su muestra sea más "LINQy" es un IEnumerable<string> para leer líneas de un archivo. He aquí una versión algo simplificada de mi clase LineReader de 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(); 
    } 
} 

Ahora puede utilizar lo siguiente:

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

Tenga en cuenta que probablemente sería más eficiente para no tener los let cláusulas:

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

en cuyo punto puede razonablemente hacerlo todo en "notación de puntos":

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

Sin embargo, ahora prefieren la legibilidad de la consulta original :)

+1

@ Jon, tengo tu libro, es genial tener una obra de arte así. Tengo una pregunta más: ¿podrías decirme la página en la que explicas con detalles el LINQ de tu libro? –

+0

Tu demasiado rápido Jon. :) –

+0

En una nota similar, aquí está la publicación del blog sobre cómo hacer que las transmisiones sean enumerables: http://www.atalasoft.com/cs/blogs/stevehawley/archive/2009/01/30/making-streams-enumerable.aspx – plinth

-1

no puede simplemente comprobar si es cierto ... Linqi Linqi es un IEnumerable<bool> (en este caso) así que tiene que marcar como Linqi.First() == true

aquí es un pequeño ejemplo:

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

También puede iterar sobre Linqi, y en mi ejemplo hay 2 elementos de la colección.

+0

Un ejemplo de utilizar tanto linq como sea posible en mi función será muy apreciado. :) –

+0

amor cuando no hay explicación para un voto negativo ... eso debería ser un requisito. – CSharpAtl

+0

ya que la pregunta era sobre SQL y LINQ No intenté reescribir completamente su código. – CSharpAtl

0

Si tuviera que volver a escribir su función de filtro usando LINQ siempre que sea posible, se vería así:

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) 
     } 
    } 
} 
+0

Hmm ... parece familiar :) (aunque no estoy seguro de por qué está seleccionando el int cuando quiere imprimir la línea) –

+0

Tenga en cuenta que leer todas las líneas de una vez es un factor limitante, e innecesariamente. (Ver mi respuesta :) –

+0

Es un problema para archivos grandes, obviamente. La respuesta de Jon Skeet es mejor ... suspiro, ¿qué más hay de nuevo? :-) –

0

Para responder a la primera cuestión, francamente, no es demasiada razón para utilizar LINQ el camino sugieres en la función anterior excepto como ejercicio. De hecho, probablemente solo hace la función más difícil de leer.

LINQ es más útil en el funcionamiento de una colección de un solo elemento, y yo lo utilizaría de esa manera en su lugar. Por lo tanto, aquí está mi intento de usar tanto LINQ posible en la función (no hacen mención de la eficiencia y yo No sugerir la lectura de todo el archivo en la memoria como este):

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

En primer lugar, habría que introducir este método:

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

Entonces, me refactorizar el método principal para usarlo. Pongo dos using declaraciones sobre el mismo bloque, y también añadió una prueba de alcance para asegurar items[1] no falla:

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

¿De qué manera no se deshace del TextReader? Está en una declaración de uso. –

+0

(Tenga en cuenta que * no * lo eliminará si un llamador llama manualmente a MoveNext() y luego abandona el iterador, pero las llamadas para redes locales se descartan automáticamente) –

+0

Tiene razón, leí mal. No está utilizando el constructor que toma un Func . Quise decir que cuando usas esa función, la clase LineReader no encapsula completamente la duración de la instancia de TextReader. –

1

cuanto a los libros de Linq, yo recomendaría:

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

Ambos son excelentes libros que el taladro en LINQ en detalle.

Para añadir otra variación al tema como-mucho-LINQ-como-posible, aquí es mi opinión:

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

Tenga en cuenta que he cambiado sus tabuladores o columnas a columnas delimitados por comas (más fácil autor en mi editor que convierte pestañas en espacios ;-)). Cuando este programa se ejecuta en un archivo de entrada:

A1,2 
B,24809,C 
C 

E 
G,24809 

la salida será:

B,24809,C 
G,24809 

podría mejorar los requisitos de memoria de esta solución mediante la sustitución de "File.ReadAllLines" y "File.WriteAllLines" con LineReader de Jon Skeet (y LineWriter en una línea similar, tomando IEnumerable y escribiendo cada elemento devuelto en el archivo de salida como una nueva línea).Esto transformaría la solución anterior de "obtener todas las líneas en la memoria como una matriz, filtrarlas, crear otra matriz en la memoria para el resultado y escribir este resultado en el archivo de salida" para "leer líneas del archivo de entrada una por una, y si eso línea cumple nuestros criterios, escríbalo en el archivo de salida inmediatamente "(enfoque de canalización).

1

He encontrado este artículo para ser extremadamente crucial para entender LINQ que se basa en tantas nuevas construcciones introducidas en .NET 3.0 & 3.5:

Te advierto que es una lectura larga, pero si de verdad Quiero entender lo que es y hace LINQ creo que es esencial

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

feliz lectura