LINQ:如何声明IEnumerable [AnonymousType]?

Iva*_*nov 24 .net c# linq

这是我的功能:

    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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

我得到一个编译错误,因为"myLine"不是IEnumerable [string]而且我不知道怎么写IEnumerable [匿名]

"无法将类型'System.Collections.Generic.IEnumerable [AnonymousType#1]'隐式转换为'System.Collections.Generic.IEnumerable [string]'"

Dan*_*ner 15

您无法声明,IEnumerable<AnonymousType>因为该类型在构建时没有(已知)名称.因此,如果要在函数声明中使用此类型,请将其设置为普通类型.或者只是修改您的查询以返回IENumerable<String>并坚持使用该类型.

或者IEnumerable<KeyValuePair<Int32, String>>使用以下select语句返回.

select new KeyValuePair<Int32, String>(id, m.Groups[2].Value)
Run Code Online (Sandbox Code Playgroud)

  • 当您说"选择新{...}"时,您创建了一个AnonymousType.如果选择一个字符串,那么它应该工作. (3认同)

jas*_*son 9

方法签名on SearchItem表示该方法返回一个IEnumerable<string>但在LINQ查询中声明的匿名类型不是类型string.如果要保留相同的方法签名,则必须将查询更改为仅选择stringseg

return myLine.Select(a => a.Text);
Run Code Online (Sandbox Code Playgroud)

如果您坚持要返回查询选择的数据,IEnumerable<object>则可以在更换return语句时返回

return myLine.Cast<object>();
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用反射消耗对象.

但实际上,如果你要在声明它的方法之外使用匿名类型,你应该定义一个类,让方法返回该类的一个IEnumerable.匿名类型是方便的,但它们可能会被滥用.


Ale*_*mes 9

我不一定推荐这个...它是类型系统的一种颠覆,但你可以这样做:

1)更改您的方法签名以返回IEnumerable(非通用签名)

2)通过示例助手添加一个强制转换:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

3)然后调用这样的方法:

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);
}
Run Code Online (Sandbox Code Playgroud)

你完成了.

关键是,如果您在两个地方创建具有相同顺序,类型和属性名称的匿名类型,则将重用这些类型.知道了这一点,您可以使用泛型来避免反射.

希望这有助于Alex

  • 正如我所说,我不推荐这个;) (4认同)
  • 聪明的。但实际上,如果有人要遇到那么多麻烦,他们应该将数据封装在一个声明的类中。 (2认同)
  • ...有趣的是,您只会遇到这个麻烦一次,即一旦您拥有辅助扩展方法,就可以一次又一次地轻松使用。不像到处滚动新类型。无论如何,这是 .NET 4.0 中 Tuple&lt;T1,T2,T3&gt; 的工作 (2认同)

jri*_*sta 6

当您正在执行的LINQ语句实际返回IEnumerable <T>时,您的函数正在尝试返回IEnumerable <string>,其中T是编译时生成的类型.匿名类型并不总是匿名的,因为它们在编译代码后采用特定的具体类型.

然而,匿名类型,因为它们在编译之前是短暂的,只能在它们创建的范围内使用.为了在您提供的示例中支持您的需求,我想最简单的解决方案是创建一个存储结果的简单实体你的查询:

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 { ... }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您的最终目标不是检索某种对象,并且您只对Path ...感兴趣,那么您仍然可以生成IEnumerable <string>:

IEnumerable<string> lines = from ... select m.Groups[2].Value;
Run Code Online (Sandbox Code Playgroud)

我希望这有助于澄清您对LINQ,enumerables和匿名类型的理解.:)

  • 仅供参考:严格说匿名类型在创建它们的范围内是唯一的并不完全正确。在同一程序集中结构等效但在两种不同方法中使用的匿名类型实际上在这些方法之间共享。有一些聪明的方法可以利用这一事实,但我不推荐它们;如果您需要在两种方法之间共享一个类型,请将其设为名义类型。 (2认同)