LINQ:如何声明IEnumerable [AnonymousType]?

时间:2009-05-23 16:28:01

标签: c# .net 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;
        }
    }

我收到编译错误,因为“myLine”不是IEnumerable [string]而且我不知道怎么写IEnumerable [匿名]

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

6 个答案:

答案 0 :(得分:15)

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

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

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

答案 1 :(得分:10)

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

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

2)添加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)然后调用这样的方法:

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

你已经完成了。

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

希望这会有所帮助 亚历

答案 2 :(得分:9)

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

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

如果您坚持要求退回查询选择的数据,则可以在用{/ 1>替换IEnumerable<object>语句时返回return

return myLine.Cast<object>();

然后你可以使用反射消耗对象。

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

答案 3 :(得分:5)

当您正在执行的LINQ语句实际返回IEnumerable&lt; T&gt;时,您的函数正在尝试返回IEnumerable&lt; string&gt;其中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 { ... }
}

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

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

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

答案 4 :(得分:0)

要记住的一件事是LINQ语句使用deferred execution。这意味着,只有在LINQ语句中对foreach语句进行迭代或在.ToList()上调用myLine方法时,return myLine; 语句才会实际执行。 /> 对于您的示例,请尝试更改:

return myLine.ToList();

要:

{{1}}

答案 5 :(得分:0)

返回Tuple而不是匿名类。 Ex(使用“命名元组”)-

(Text: line, ItemId: id, Path: m.Groups[2].Value)

https://docs.microsoft.com/en-us/dotnet/csharp/tuples

代替-

new { Text = line, ItemId = id, Path = m.Groups[2].Value }