【问题标题】:IComparer<T> child as a generic param could not be used in SortIComparer<T> 子作为通用参数不能在排序中使用
【发布时间】:2014-12-27 03:35:38
【问题描述】:

在 C# 中尝试对我的 List&lt;&gt; 进行排序时遇到了困难。

我有一个接口类型及其实现类:

public interface IRoom
{
    // ...
}

public class Room : IRoom
{
    // ...
}

还有一个基类供我的比较器使用:

public abstract class RoomComparer : IComparer<IRoom>
{
    public RoomComparer()
    {
    }

    public abstract int Compare(IRoom x, IRoom y);
}

还有它的两个孩子:

public class StandardMultipliedSquareMetersComparer : RoomComparer
{
    public StandardMultipliedSquareMetersComparer()
    {
    }

    public override int Compare(IRoom a, IRoom b)
    {
        // ...
    }
}

public class SquareMetersComparer : RoomComparer
{
    public SquareMetersComparer()
    {
    }

    public override int Compare(IRoom a, IRoom b)
    {
        // ...
    }
}

现在,问题从这里开始:我得到了一个泛型类 Hotel,其中包含我的 Room 实例列表:

public class Hotel<TRoom, TComparer> : IHotel, IEnumerable<TRoom> 
    where TRoom : IRoom 
    where TComparer : RoomComparer, new()
{
    public List<TRoom> Rooms;
    protected TComparer Comparer;

    public Hotel(TRoom[] rooms)
    {
        Rooms = new List<TRoom>();
        Rooms.AddRange(rooms);
        Comparer = new TComparer();
        Rooms.Sort(Comparer);
    }
}

这就是问题所在 - 我在Rooms.Sort(Comparer); 线上遇到了两个错误:

错误 CS1502:最佳重载方法匹配 `System.Collections.Generic.List.Sort(System.Collections.Generic.IComparer)' 有一些无效参数 (CS1502)

错误 CS1503:参数 #1' cannot convertTComparer' 表达式为 键入“System.Collections.Generic.IComparer”(CS1503)

我尝试了许多不同的解决方案,但没有结果。发生了什么事?

注意:在 Ubuntu 上使用 Mono 3.10.0

【问题讨论】:

    标签: c# sorting generics mono


    【解决方案1】:

    您正在尝试为此使用通用逆变(如果比较器可以比较两个 TRoom 值,它应该能够比较两个 IRoom 值),这通常没问题- 但仅当 TRoom 已知为类类型时。您可以通过要求 TRoom 是引用类型来解决此问题:

    where TRoom : class, IRoom
    

    此时,逆变工作正常,一切都很好。当然,它确实要求您所有的房间类型都是真正的等级。

    您肯定需要酒店像这样通用吗?这对我来说似乎有点太过分了......

    【讨论】:

    • 为什么不适合值类型?
    • @Stilgar:泛型变体的工作原理是相同的代码可用于所有适当的泛型类型参数,而值不需要经过任何转换(例如从结构到接口的装箱)。这只是泛型变量在 .NET 中工作方式的一个限制。
    • 所以基本上编译器必须确定它不需要生成装箱代码?
    • @Stilgar:或任何其他类型的实际更改位的转换。这个想法是 string 引用 一个对象引用,因此 IComparer&lt;object&gt; 可以用于对 List&lt;string&gt; 进行排序,例如。
    • 很遗憾,但我真的需要那种复杂性 =(
    【解决方案2】:

    以下是解决此问题的方法:

    where TComparer : IComparer<IRoom>, new()
    

    (至于为什么,见乔恩的回答)

    你也可以稍微简化你的代码:

    Rooms = rooms.OrderBy(room => room, new TComparer()).ToList();
    

    (只需添加using System.Linq

    See working demo

    【讨论】:

    • RoomComparer 不一定满足该约束 - 如果您有 public struct ValueRoom : IRoom,那么普通的 RoomComparer 不会实现 IComparer&lt;ValueRoom&gt;。 (当然,那时我的解决方案也行不通。)
    • @Jon 感谢您指出这一点,我没想过拳击
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-24
    • 2012-12-29
    • 2020-09-14
    • 2019-08-23
    • 2010-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多