2009-05-23 7 views
22

Dies ist meine Funktion:LINQ: Wie IEnumerable [AnonymousType] deklarieren?

private IEnumerable<string> SeachItem(int[] ItemIds) 
    { 
     using (var reader = File.OpenText(Application.StartupPath + @"\temp\A_A.tmp")) 
     { 
      var myLine = from line in ReadLines(reader) 
         where line.Length > 1 
         let id = int.Parse(line.Split('\t')[1]) 
         where ItemIds.Contains(id) 
         let m = Regex.Match(line, @"^\d+\t(\d+)\t.+?\t(item\\[^\t]+\.ddj)") 
         where m.Success == true 
         select new { Text = line, ItemId = id, Path = m.Groups[2].Value }; 
      return myLine; 
     } 
    } 

ich einen Compiler-Fehler erhalten, weil "Myline" kein IEnumerable [string] ist, und ich weiß nicht, wie IEnumerable [Anonymous]

„Kann nicht schreiben implizit Typ 'System.Collections.Generic.IEnumerable [AnonymousType # 1]' auf 'System.Collections.Generic.IEnumerable [string]“

Antwort

15

Sie nicht IEnumerable<AnonymousType> erklären kann, weil der Typ keine (bekannten) Namen zum Zeitpunkt der Erstellung hat. Wenn Sie diesen Typ in einer Funktionsdeklaration verwenden möchten, sollten Sie einen normalen Typ festlegen. Oder ändern Sie einfach Ihre Abfrage, um eine IENumerable<String> zurückzugeben und bei diesem Typ zu bleiben.

Oder geben Sie IEnumerable<KeyValuePair<Int32, String>> mithilfe der folgenden SELECT-Anweisung zurück.

select new KeyValuePair<Int32, String>(id, m.Groups[2].Value) 
+2

Wenn Sie "Select new {...}" sagen, haben Sie einen AnonymousType erstellt. Wenn Sie einen String auswählen, dann –

+0

Nein, aber Sie können IEnumerable zurückgeben und den CastByExample-Trick verwenden Nicht empfohlen –

0

Eine Sache zu erinnern ist, dass konvertieren LINQ Aussagen deferred execution verwenden. Das bedeutet, dass Ihre LINQ-Anweisung nicht tatsächlich ausgeführt wird, bis Sie entweder in einer foreach-Anweisung darüber iterieren oder die .ToList()-Methode unter myLine aufrufen. versuchen
Für Ihr Beispiel zu ändern:

return myLine; 

An:

return myLine.ToList(); 
+0

Das Problem in der Prozedurdefinition ist, so sollte es "private List SeachItem (int [] ItemIDs)" sein, aber das funktioniert auch nicht. "Umwandeln kann nicht implizit Typ List [AnoynymousType # 1] to List [string] .Bitte sagen Sie mir, ob es nicht möglich ist. :) –

8

Die Methodensignatur auf SearchItem zeigt an, dass das Verfahren ein IEnumerable<string> gibt aber den anonymen Typ in Ihrer LINQ-Abfrage deklarierte ist nicht vom Typ string. Wenn Sie dieselbe Methodensignatur beibehalten möchten, müssen Sie Ihre Abfrage so ändern, dass nur string s ausgewählt wird. z.B.

return myLine.Select(a => a.Text); 

Wenn Sie darauf bestehen, auf die Daten durch Ihre Frage ausgewählt Rückkehr, können Sie eine IEnumerable<object> zurück, wenn Sie Ihre return Anweisung ersetzen mit

return myLine.Cast<object>(); 

Dann können Sie die Objekte mithilfe von Reflektion verbrauchen.

Aber wirklich, wenn Sie einen anonymen Typ außerhalb der Methode verbrauchen, in der es deklariert wird, sollten Sie eine Klasse definieren und die Methode IEnumerable dieser Klasse zurückgeben. Anonyme Arten sind Bequemlichkeit, aber sie unterliegen Missbrauch.

5

Ihre Funktion versucht IEnumerable <String>, zurückzukehren, wenn die LINQ-Anweisung Sie tatsächlich ausführen, um ein IEnumerable <T> zurückkehrt, wo T eine Kompilierung-erzeugten Typ ist. Anonyme Typen sind nicht immer anonym, da sie nach der Kompilierung des Codes einen konkreten Typ annehmen.

Anonyme Typen können jedoch nur in dem Umfang verwendet werden, in dem sie erstellt wurden, da sie kurzlebig sind bis sie kompiliert wurden.Um Ihre Bedürfnisse im Beispiel zu unterstützen Sie zur Verfügung gestellt, würde ich die einfachste Lösung, sagen ist eine einfache Einheit zu schaffen, die die Ergebnisse der Abfrage speichert:

public class SearchItemResult 
{ 
    public string Text { get; set; } 
    public int ItemId { get; set; } 
    public string Path { get; set; } 
} 

public IEnumerable<SearchItemResult> SearchItem(int[] itemIds) 
{ 
    // ... 
    IEnumerable<SearchItemResult> results = from ... select new SearchItemResult { ... } 
} 

Allerdings, wenn Ihr Ziel ist nicht eine Art abzurufen

IEnumerable<string> lines = from ... select m.Groups[2].Value; 

ich hoffe, dass Ihr Verständnis von LINQ zu klären hilft, enumerables und anonyme Typen: Objekt, und Sie sind nur daran interessiert, sagen wir, den Weg ... dann können Sie immer noch eine IEnumerable <Zeichenfolge> erzeugen. :)

+1

Zu Ihrer Information: Es ist nicht streng korrekt zu sagen, dass anonyme Typen für den Bereich, in dem sie erstellt werden, eindeutig sind äquivalent in der gleichen a Assembly, aber in zwei verschiedenen Methoden verwendet werden tatsächlich über diese Methoden gemeinsam. Es gibt kluge Wege, diese Tatsache auszunutzen, aber ich würde sie nicht empfehlen; Wenn Sie einen Typ für zwei Methoden freigeben müssen, machen Sie ihn nominal. –

+0

Ist das Eric Lippert von Microsoft berühmt? Jetzt wo du die Tasche geöffnet hast, musst du die Katze rauslassen. Wie teilen Sie einen anonymen Typ bereichsübergreifend? ;) – jrista

+1

Sehr sorgfältig. Sie müssen die Tricks der Infotyp-Tricks und die erfassten äußeren Variablen verwenden. Vielleicht mache ich irgendwann mal einen Blog darüber. –

6

Ich bin nicht unbedingt diese Empfehlung ... Es ist eine Art der Subversion des Typs System, aber man könnte dies tun: Ihre Methode

1) Signatur ändern IEnumerable (die nicht generische zurückzukehren)

2) fügen Sie einen cast by example Helfer:

public static class Extensions{ 
    public static IEnumerable<T> CastByExample<T>(
      this IEnumerable sequence, 
      T example) where T: class 
    { 
     foreach (Object o in sequence) 
      yield return o as T; 
    } 
} 

3) dann die Methode in etwa so nennen:

var example = new { Text = "", ItemId = 0, Path = "" }; 
foreach (var x in SeachItem(ids).CastByExample(example)) 
{ 
    // now you can access the properties of x 
    Console.WriteLine("{0},{1},{2}", x.Text, x.ItemId, x.Path); 
} 

Und Sie sind fertig.

Der Schlüssel dazu ist die Tatsache, dass die Typen wiederverwendet werden, wenn Sie einen anonymen Typ mit der gleichen Reihenfolge, Typen und Eigenschaftsnamen an zwei Stellen erstellen. Wenn Sie dies wissen, können Sie Generika verwenden, um Reflexionen zu vermeiden.

hoffe, das hilft Alex

+0

Clever. Aber wenn jemand so viel Ärger machen will, sollte er die Daten einfach in eine deklarierte Klasse kapseln. – jason

+2

Wie gesagt, ich empfehle das nicht;) –

+0

... Interessanterweise gehen Sie nur einmal zu diesem Problem, d. H. Sobald Sie die Helfer-Extension-Methode haben, ist diese einfach wieder und wieder zu benutzen. Im Gegensatz zu neuen Typen überall zu rollen. Wie auch immer, das ist ein Job für Tuple in .NET 4.0 –

Verwandte Themen