【问题标题】:can't pass default comparer as IComparer<object>无法将默认比较器作为 IComparer<object> 传递
【发布时间】:2009-09-03 18:59:08
【问题描述】:

我正在尝试使用代码调用需要IComparer&lt;object&gt; 类型参数的“排序”方法:

collection.Sort((IComparer<object>)Comparer<DateTime>.Default)

它会构建,但在运行时我会收到 InvalidCastException 消息:

Unable to cast object of type
'System.Collections.Generic.GenericComparer`1[System.DateTime]'
to type 'System.Collections.Generic.IComparer`1[System.Object]'.

现在呢?

【问题讨论】:

  • 请张贴“收藏”声明。我们需要更多信息来提供帮助。
  • 这是一个 LLBL 集合,即 SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2(现在你很抱歉你问了 :)
  • 您需要一个自定义比较器 - 如果类型始终是 DateTIme 值,那么我刚刚添加的应该可以工作(只需 5 行额外的代码)
  • ... 另外,不,这在 C# 4.0 中也不起作用(因为,虽然 IComparer&lt;T&gt; 是逆变的,但它不会是协变的 - 并且此代码试图将其视为协变)。
  • @Pavel:还因为 DateTime 是一种值类型,并且方差仅适用于引用类型:)

标签: c# generics llblgenpro


【解决方案1】:

如果您想要的只是默认比较,这将起作用:

collection.Sort(Comparer<object>.Default)

Comparer.Default 使用对象的固有比较语义(即 IComparable.CompareTo)。

【讨论】:

    【解决方案2】:

    很遗憾,您需要有一个适当类型的比较器。

    您可以创建一个自定义 IComparer&lt;object&gt; 类来包装 DateTime 比较器,但无法通过强制转换直接执行此操作。

    如果您的集合始终包含 DateTime 对象,那么您可以这样做:

    ICollection<DateTime> collection = ...;
    collection.Sort(Comparer<DateTime>.Default); // or just collection.Sort()
    

    阅读评论后编辑:

    如果您直接使用 ICollection,您可能希望使用 LINQ 选项来执行以下操作:

    collection.Cast<DateTime>().OrderBy( date => date );
    

    如果您正在使用实现IList&lt;T&gt; 的东西(例如List&lt;DateTime&gt;),您可以在列表本身上调用Sort()。


    由于您使用的是非标准类,因此您需要制作自己的比较器:

    class Comparer : IComparer<object> {
        int Compare(object a, object b) {
             return DateTime.Compare((DateTime)a, (DateTime)b);
        }
    }
    

    然后您可以调用:

    collection.Sort(new Comparer() );
    

    【讨论】:

    • 我得到:'System.Collections.Generic.ICollection' 不包含'Sort' 的定义,并且没有扩展方法'Sort' 接受'System.可以找到 Collections.Generic.ICollection'(您是否缺少 using 指令或程序集引用?)
    • LINQ 选项不起作用,因为我想要一个“内存中”排序(LINQ OrderBy 返回一个新集合)。至于第二个建议,不,它没有实现 IList :(
    • 您的收藏是什么类型的?你能把它的声明放在适当的位置吗?
    • 再次编辑,因为我们知道集合类型。
    • C# 4.0 中的 co 和 contra 变化万岁!
    【解决方案3】:

    如果您可以更改集合对象的类型(即从List&lt;object&gt; 更改为ArrayList),那么您可以只使用非泛型IComparer 接口(由Comparer&lt;DateTime&gt;.Default 实现)。

    如果你不能改变集合对象的类型,那么你就不走运了。 (您总是可以实现一个实现IComparer&lt;object&gt; 的对象,如下所示:

     public class DateTimeComparer : IComparer<object>
        {       
            public int Compare(object x, object y)
            {
                IComparer myComparer = Comparer<DateTime>.Default as IComparer;
                return myComparer.Compare(x, y);
            }     
        }
    

    (如果您的集合中有任何非 DateTime 项目,这将引发异常)

    编辑:

    您还可以实现一些更安全的类型,即:

      public class DateTimeComparer : IComparer<object>
        {       
            public int Compare(object x, object y)
            {
                if ( x is DateTime && y is DateTime )
                {
                    return Comparer<DateTime>.Default.Compare((DateTime) x, (DateTime) y);
                }
                else
                {
                    // handle the type mismatch
                }
            }     
        }
    

    【讨论】:

      【解决方案4】:

      你可以像这样定义一个扩展函数:

      public static class ComparerConversion
          {
              private class ComparerWrapper<T> : IComparer<object>
              {
                  private readonly IComparer<T> comparer;
      
                  public ComparerWrapper(IComparer<T> comparer)
                  {
                      this.comparer = comparer;
                  }
      
                  public int Compare(object x, object y)
                  {
                      return this.comparer.Compare((T)x, (T)y);
                  }
              }
      
              public static IComparer<object> ToObjectComparer<T>(this IComparer<T> comparer)
              {
                  return new ComparerWrapper<T>(comparer);
              }
          }
      

      并像这样使用它:

      List<object> collection = new List<object> { DateTime.Now.AddDays(1), DateTime.Now };
      collection.Sort(Comparer<DateTime>.Default.ToObjectComparer());
      

      【讨论】:

        【解决方案5】:

        只需尝试删除强制转换并让编译器选择IComparer 而不是IComparer&lt;T&gt;

        Comparer&lt;T&gt; 实现了 IComparer&lt;T&gt;IComparer,所以它应该可以工作。

        这很好用:

        ArrayList collection = new ArrayList {DateTime.Now.AddDays(1), DateTime.Now};
        collection.Sort(Comparer<DateTime>.Default);
        

        【讨论】:

        • 我得到:无法从 'System.Collections.Generic.Comparer' 转换为 'System.Collections.Generic.IComparer
        • 您是否尝试使用具有此 Sort(IComparer comparer>) 签名或 Sort(IComparer comparer) 的 Sort 函数?您的收藏类型是什么?
        • 它需要 IComparer。这是一个 LLBL 集合,即 SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2 (现在你很抱歉你问了 :)
        • 您唯一的选择是创建包装器
        猜你喜欢
        • 2018-05-22
        • 1970-01-01
        • 1970-01-01
        • 2015-06-30
        • 2020-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多