【问题标题】:Having some confusion with LINQ对 LINQ 有一些困惑
【发布时间】:2008-11-06 08:31:08
【问题描述】:

一些背景信息;

  • LanguageResource 是基类
  • LanguageTranslatorResource 和 LanguageEditorResource 继承自 LanguageResource
  • LanguageEditorResource 定义了一个 IsDirty 属性
  • LanguageResourceCollection 是 LanguageResource 的集合
  • LanguageResourceCollection 在内部将 LanguageResources 保存在 Dictionary<string, LanguageResource> _dict
  • LanguageResourceCollection.GetEnumerator() 返回_dict.Values.GetEnumerator()

我有一个 LanguageResourceCollection _resources,它只包含 LanguageEditorResource 对象,并且想使用 LINQ 来枚举那些脏的,所以我尝试了以下方法。我的具体问题以粗体显示。

  1. _resources.Where(r => (r as LanguageEditorResource).IsDirty)

    Intellisense 不会显示其他 LINQ 方法,但我还是对其进行了编码,并被告知“LanguageResourceCollection 不包含 'Where' 的定义并且没有扩展方法......”。

    为什么LanguageResourceCollection 实现 IEnumerable 的方式使其无法支持 LINQ?

  2. 如果我将查询更改为

    (_resources as IEnumerable<LanguageEditorResource>).Where(r => r.IsDirty)

    Intellisense 显示 LINQ 方法并编译解决方案。但是在运行时我得到一个 ArgumentNullException “值不能为空。参数名称:源”。

    这是我的 LINQ 代码中的问题吗?
    类的一般设计有问题吗?
    如何深入了解 LINQ 生成的内容以尝试查看问题所在?

我对这个问题的目的不是为特定问题找到解决方案,因为我现在必须使用其他(非 LINQ)方法来解决它,而是尝试提高我对 LINQ 的理解并学习如何做改进我的类设计以更好地使用 LINQ。

【问题讨论】:

    标签: c# .net linq linq-to-objects


    【解决方案1】:

    听起来您的集合实现了IEnumerable,而不是IEnumerable<T>,因此您需要:

    _resources.Cast<LanguageEditorResource>().Where(r => r.IsDirty)
    

    注意Enumerable.Where 是在IEnumerable&lt;T&gt; 上定义的,而不是IEnumerable - 如果您有非泛型类型,则需要使用Cast&lt;T&gt;(或OfType&lt;T&gt;)来获得正确的类型。不同之处在于Cast&lt;T&gt; 如果发现不是T 的东西,就会抛出异常,而OfType&lt;T&gt; 只是忽略任何不是T 的东西。由于您已声明您的集合包含LanguageEditorResource,因此使用Cast&lt;T&gt; 检查该假设是合理的,而不是静默删除数据。

    还要检查您是否“使用 System.Linq”(并且正在引用 System.Core(.NET 3.5;否则 LINQBridge 与 .NET 2.0)以获取 Where 扩展方法。

    实际上,让您的集合实现 IEnumerable&lt;LanguageResource&gt; 是值得的 - 您可以非常简单地使用 Cast&lt;T&gt; 方法或迭代器块 (yield return)。

    [编辑] 以 Richard Poole 的说明为基础 - 您可以在此处编写 自己的 通用容器,大概使用 T : LanguageResource(并在 Dictionary&lt;string,T&gt; 中使用 T,并实现 IEnumerable&lt;T&gt;ICollection&lt;T&gt; )。只是一个想法。

    【讨论】:

      【解决方案2】:

      除了 Marc G 的回答之外,如果您能够这样做,您可能需要考虑放弃您的自定义 LanguageResourceCollection 类以支持通用 List&lt;LanguageResource&gt;。这将解决您当前的问题并摆脱讨厌的 .NET 1.1ish 自定义集合。

      【讨论】:

      • 好吧,它仍然需要从 LanguageResource 获取到 LanguageEditorResource,并注意 Dictionary 的内部使用 - 它只是一个基本的集合包装器。
      【解决方案3】:

      我如何深入了解 LINQ 生成的内容以尝试查看问题所在?

      Linq 在这里没有生成任何东西。您可以使用调试器单步执行。

      尝试提高我对 LINQ 的理解,并了解如何改进我的类设计以更好地使用 LINQ。

      System.Linq.Enumerable 方法严重依赖 IEnumerable 协定。您需要了解您的类如何生成支持此协定的目标。 T代表的类型很重要!

      您可以将此方法添加到 LanguageResourceCollection:

      public IEnumerable<T> ParticularResources<T>()
      {
        return _dict.Values.OfType<T>();
      }
      

      并通过以下方式调用它:

      _resources
        .ParticularResources<LanguageEditorResource>()
        .Where(r => r.IsDirty)
      

      如果集合类没有针对相同的 _dict.Values 实现 IEnumerable,则此示例将更有意义。重点是理解 IEnumerable 和泛型类型。

      【讨论】:

        猜你喜欢
        • 2020-11-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-12
        • 2014-06-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多