2009-05-23 13 views
22

C'est ma fonction:LINQ: Comment déclarer IEnumerable [AnonymousType]?

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

Je reçois une erreur de compilation, parce que "myLine" est pas une [chaîne] IEnumerable et je ne sais pas comment écrire IEnumerable [Anonymous]

« Can not convertir implicitement le type 'System.Collections.Generic.IEnumerable [AnonymousType # 1]' à 'System.Collections.Generic.IEnumerable [string]' »

Répondre

15

Vous ne pouvez pas déclarer IEnumerable<AnonymousType> parce que le type n'a pas de nom (connu) au moment de la construction. Donc, si vous voulez utiliser ce type dans une déclaration de fonction, faites-en un type normal. Ou simplement modifier votre requête pour retourner un IENumerable<String> et rester avec ce type.

Ou renvoyez IEnumerable<KeyValuePair<Int32, String>> en utilisant l'instruction de sélection suivante.

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

Lorsque vous dites "select new {...}", vous avez fait un AnonymousType Si vous sélectionnez une chaîne, puis cela devrait fonctionner –

+0

Non mais vous pouvez retourner IEnumerable et utiliser l'astuce CastByExample Non recommandé –

0

Une chose à retenir est que LINQ déclarations utilisent deferred execution. Cela signifie que votre instruction LINQ ne s'exécute pas réellement tant que vous ne l'avez pas itérée dans une instruction foreach ou que vous n'avez pas appelé la méthode .ToList() sur myLine.
Pour votre exemple, essayez de changer:

return myLine; 

Pour:

return myLine.ToList(); 
+0

Le problème est dans la définition de la procédure, il devrait donc être « Liste privée SeachItem (int [] itemID) », mais cela ne fonctionne pas non plus. "Impossible de convertir implicitement le type Liste [AnoynymousType # 1] à List [string]. S'il vous plaît dites-moi si c'est juste impossible à faire. :) –

8

La signature de la méthode sur SearchItem indique que la méthode retourne un IEnumerable<string> mais le type anonyme déclaré dans votre requête LINQ est pas de type string. Si vous souhaitez conserver la même signature de méthode, vous devez modifier votre requête pour sélectionner uniquement string s. par exemple.

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

Si vous insistez pour retourner les données sélectionnées par votre requête, vous pouvez retourner un IEnumerable<object> si vous remplacez votre déclaration return avec

return myLine.Cast<object>(); 

Ensuite, vous pouvez consommer les objets à l'aide de réflexion. Mais en réalité, si vous allez consommer un type anonyme en dehors de la méthode dans laquelle il est déclaré, vous devez définir une classe et retourner la méthode IEnumerable de cette classe. Les types anonymes sont pratiques, mais ils sont sujets à des abus.

5

Votre fonction tente de revenir IEnumerable < chaîne >, lorsque l'instruction LINQ vous exécutez est en fait un retour IEnumerable <T> où T est un type généré à la compilation. Les types anonymes ne sont pas toujours anonymes, car ils prennent un type concret spécifique après la compilation du code.

Cependant, les types anonymes, puisqu'ils sont éphémères jusqu'à la compilation, ne peuvent être utilisés que dans la portée dans laquelle ils sont créés.Pour répondre à vos besoins dans l'exemple que vous avez fourni, je dirais que la solution la plus simple est de créer une entité simple qui stocke les résultats de votre recherche:

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 { ... } 
} 

Cependant, si votre but ultime est de ne pas récupérer une sorte de objet, et vous ne souhaitez que, par exemple, le chemin ... vous pouvez toujours générer une chaîne <IEnumerable>:

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

J'espère que cela aide à clarifier votre compréhension de LINQ, enumerables et types anonymes. :)

+1

FYI: Il n'est pas strictement correct de dire que les types anonymes sont uniques à la portée dans laquelle ils sont créés. Types anonymes structurellement équivalent dans le même un ssembly mais utilisé dans deux méthodes différentes sont réellement partagées entre ces méthodes. Il existe des moyens astucieux de tirer parti de ce fait, mais je ne les recommanderais pas; Si vous devez partager un type entre deux méthodes, rendez-le nominal. –

+0

Est-ce Eric Lippert de Microsoft? Maintenant que tu as ouvert le sac, tu dois laisser sortir le chat. Comment partagez-vous un type anonyme sur plusieurs étendues? ;) – jrista

+1

Très attentivement. Vous devez utiliser des astuces d'inférence de type méthode goofy et des variables externes capturées. Peut-être que je vais faire un blog sur ce sujet un certain temps. –

6

Je ne suis pas nécessairement le recommander ... Il est une sorte de subversion du système de type mais vous pouvez le faire:

1) changer votre signature de méthode pour retourner IEnumerable (celui non générique)

2) ajouter une aide cast by example:

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) puis appeler la méthode quelque chose comme ceci:

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

Et vous avez terminé.

La clé est que si vous créez un type anonyme avec le même ordre, les types et les noms de propriété à deux endroits, les types seront réutilisés. Sachant cela, vous pouvez utiliser des génériques pour éviter la réflexion.

Hope this helps Alex

+0

Clever. Mais vraiment, si quelqu'un va avoir autant de problèmes, il devrait simplement encapsuler les données dans une classe déclarée. – jason

+2

Comme je l'ai dit je ne le recommande pas;) –

+0

... intéressant, vous allez seulement à ce problème une fois, c'est-à-dire une fois que vous avez la méthode d'extension d'aide, il est facile à utiliser encore et encore. Contrairement au roulement de nouveaux types partout. Quoi qu'il en soit, c'est un travail pour Tuple dans .NET 4.0 –

Questions connexes