【问题标题】:LINQ: How to declare IEnumerable[AnonymousType]?LINQ:如何声明 IEnumerable[AnonymousType]?
【发布时间】:2009-05-23 16:28:00
【问题描述】:

这是我的功能:

    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[Anonymous]

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

【问题讨论】:

    标签: c# .net linq


    【解决方案1】:

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

    或者使用下面的 select 语句返回 IEnumerable&lt;KeyValuePair&lt;Int32, String&gt;&gt;

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

    【讨论】:

    • 当您说“select new {...}”时,您创建了一个 AnonymousType。如果选择一个字符串,那么它应该可以工作。
    • 否,但您可以返回 IEnumerable 并使用 CastByExample 技巧。不过不推荐。
    【解决方案2】:

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

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

    你就完成了。

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

    希望这会有所帮助 亚历克斯

    【讨论】:

    • 聪明。但实际上,如果有人要搞这么多麻烦,他们应该将数据封装在声明的类中。
    • 正如我所说,我不推荐这个 ;)
    • ... 有趣的是,您只需遇到一次这种麻烦,即一旦您有了辅助扩展方法,它就很容易一次又一次地使用。不像到处滚动新类型。无论如何,这是 .NET 4.0 中 Tuple 的工作
    【解决方案3】:

    SearchItem 上的方法签名表明该方法返回一个IEnumerable&lt;string&gt;,但在您的 LINQ 查询中声明的匿名类型不是string 类型。如果要保留相同的方法签名,则必须将查询更改为仅选择 strings。例如

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

    如果您坚持返回查询选择的数据,如果您将 return 语句替换为

    ,则可以返回 IEnumerable&lt;object&gt;
    return myLine.Cast<object>();
    

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

    但实际上,如果您要在声明它的方法之外使用匿名类型,您应该定义一个类并让该方法返回该类的 IEnumerable。匿名类型很方便,但容易被滥用。

    【讨论】:

      【解决方案4】:

      您的函数试图返回 IEnumerable,而您正在执行的 LINQ 语句实际上返回的是 IEnumerable,其中 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 { ... }
      }
      

      但是,如果您的最终目标不是检索某种对象,并且您只对路径感兴趣...那么您仍然可以生成一个 IEnumerable

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

      我希望这有助于阐明您对 LINQ、可枚举和匿名类型的理解。 :)

      【讨论】:

      • 仅供参考:说匿名类型在创建它们的范围内是唯一的并不完全正确。在同一程序集中结构上等效但用于两种不同方法的匿名类型实际上是共享的跨越这些方法。有一些巧妙的方法可以利用这一事实,但我不推荐它们;如果您需要在两种方法之间共享一个类型,请将其设为名义型。
      • 那是微软名声大噪的 Eric Lippert 吗?现在你已经打开了袋子,你得把猫放出来了。您如何跨范围共享匿名类型? ;)
      • 非常小心。您必须使用愚蠢的方法类型推断技巧和捕获的外部变量。也许我会在某个时候写一篇博客。
      • 我发现这个答案很有用。
      【解决方案5】:

      返回 ValueTuple 而不是匿名类。例如(使用“命名元组”)-

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

      ValueTuple 是 C# 版本 7 的一部分,最初是作为单独的 NuGet 包 (System.ValueTuple) 实现的。从 .NET 4.7 开始,它是一种内置类型。对于 .NET Core,2.0 之前的版本需要 NuGet 包,但它是 2.0 版内置的。

      【讨论】:

        【解决方案6】:

        要记住的一点是LINQ 语句使用deferred execution。这意味着您的LINQ 语句实际上不会执行,除非您在foreach 语句中对其进行迭代,或者在myLine 上调用.ToList() 方法。
        对于您的示例,请尝试更改:

        return myLine;
        

        收件人:

        return myLine.ToList();
        

        【讨论】:

        • 问题出在程序定义中,所以应该是“private List SeachItem(int[] ItemIds)”,但这也不起作用。“不能隐式转换类型List[ AnoynymousType#1] 到 List[string]。请告诉我它是否不可能完成。:)
        【解决方案7】:

        此链接可能对最终来到这里的其他人有用 https://idreesdotnet.blogspot.com/2019/08/c-how-to-create-list-of-anonymous-type.html

        第一个解决方案(共 6 个)非常简单

        1:首先创建匿名类型的对象,然后将其传递给数组并调用 ToList() 方法。

           var o1 = new { Id = 1, Name = "Foo" };
           var o2 = new { Id = 2, Name = "Bar" };
           var list = new[] { o1, o2 }.ToList();
        

        【讨论】:

        • 它没有回答如何从方法返回列表的问题。问题不仅仅是标题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多