通用 OrderBy() 调用中缺少 IComparable 接口的编译时检测
Compile-time detection of missing IComparable interface in generic OrderBy() call
当列表中的对象未实现 IComparable 接口时,通用方法 OrderBy() 或 ThenBy() 仅在 运行 时失败。就像这个例子:
public class DataClass
{
public int Value { get; set; }
public DataClass(int value)
{
Value = value;
}
}
void Test()
{
var list = new List<DataClass>() { new DataClass(10), new DataClass(1), new DataClass(5) };
var sortedList = list.OrderBy(data => data).ToList(); // InvalidOperationException thrown by OrderBy()
}
有没有办法在编译时检测到这个问题?
通过检测问题我的意思是编译器向我显示 OrderBy() 调用中使用的 lambda 函数没有 return 实现 IComparable 接口的对象的错误。我想查看编译时错误,而不是 运行 次错误。
我在此处寻找按易于实施排序的最便宜的修复程序:
- 打开一些编译器标志,警告我传递给 OrderBy() 的对象没有必需的接口。
- 使用某种可以检测缺失接口的代码分析器。
- 如果#1 和#2 不可行,则将 LINQ 扩展方法替换为现有的相当流行的 Nuget 包中的方法。
- 最后一个选项是将未明确定义所需接口的 LINQ 方法替换为明确定义所需接口的方法。
当我回答这个问题时,我想我可能已经找到了这个约束不存在的原因:keySelector
中 Key
的类型可以实现 或者 IComaprable<TKey>
或 IComparable
,而且我没有办法在约束中使用 OR!
因此,鉴于这些信息,如果您想编写自己的扩展方法来添加类型约束,您要么必须为每个重载编写两个(具有不同的名称),要么您只需要必须选择将 TKey
类型约束到哪个接口。
如果可以接受,请继续阅读我的原始答案...
我认为此方法出现 compile-time 错误的唯一方法是将其包装在您自己的扩展方法中,在调用 Linq
之前添加 IComparable<T>
约束方法:
public static class Extensions
{
public static IEnumerable<TSource> OrderByConstrained<TSource, TKey>(
this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
where TKey : IComparable<TKey>
{
return source.OrderBy(keySelector);
}
}
现在你会得到一个编译时错误,直到 Data
class 实现 IComparable<DataClass>
:
var sortedList = list.OrderByConstrained(data => data);
// Compile Error CS0311: The type 'Test.Program.DataClass' cannot be used as type
// parameter 'TKey' in the generic type or method
// 'Extensions.OrderByConstrained<TSource, TKey>
// (IEnumerable<TSource>, Func<TSource, TKey>)'.
// There is no implicit reference conversion from 'Test.Program.DataClass' to
// 'System.IComparable<Test.Program.DataClass>'.
然后实现接口将解决编译错误:
public class DataClass : IComparable<DataClass>
{
public int Value { get; set; }
public DataClass(int value)
{
Value = value;
}
public int CompareTo(DataClass other)
{
return other == null ? 1 : Value.CompareTo(other.Value);
}
}
当列表中的对象未实现 IComparable 接口时,通用方法 OrderBy() 或 ThenBy() 仅在 运行 时失败。就像这个例子:
public class DataClass
{
public int Value { get; set; }
public DataClass(int value)
{
Value = value;
}
}
void Test()
{
var list = new List<DataClass>() { new DataClass(10), new DataClass(1), new DataClass(5) };
var sortedList = list.OrderBy(data => data).ToList(); // InvalidOperationException thrown by OrderBy()
}
有没有办法在编译时检测到这个问题?
通过检测问题我的意思是编译器向我显示 OrderBy() 调用中使用的 lambda 函数没有 return 实现 IComparable 接口的对象的错误。我想查看编译时错误,而不是 运行 次错误。
我在此处寻找按易于实施排序的最便宜的修复程序:
- 打开一些编译器标志,警告我传递给 OrderBy() 的对象没有必需的接口。
- 使用某种可以检测缺失接口的代码分析器。
- 如果#1 和#2 不可行,则将 LINQ 扩展方法替换为现有的相当流行的 Nuget 包中的方法。
- 最后一个选项是将未明确定义所需接口的 LINQ 方法替换为明确定义所需接口的方法。
当我回答这个问题时,我想我可能已经找到了这个约束不存在的原因:keySelector
中 Key
的类型可以实现 或者 IComaprable<TKey>
或 IComparable
,而且我没有办法在约束中使用 OR!
因此,鉴于这些信息,如果您想编写自己的扩展方法来添加类型约束,您要么必须为每个重载编写两个(具有不同的名称),要么您只需要必须选择将 TKey
类型约束到哪个接口。
如果可以接受,请继续阅读我的原始答案...
我认为此方法出现 compile-time 错误的唯一方法是将其包装在您自己的扩展方法中,在调用 Linq
之前添加 IComparable<T>
约束方法:
public static class Extensions
{
public static IEnumerable<TSource> OrderByConstrained<TSource, TKey>(
this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
where TKey : IComparable<TKey>
{
return source.OrderBy(keySelector);
}
}
现在你会得到一个编译时错误,直到 Data
class 实现 IComparable<DataClass>
:
var sortedList = list.OrderByConstrained(data => data);
// Compile Error CS0311: The type 'Test.Program.DataClass' cannot be used as type
// parameter 'TKey' in the generic type or method
// 'Extensions.OrderByConstrained<TSource, TKey>
// (IEnumerable<TSource>, Func<TSource, TKey>)'.
// There is no implicit reference conversion from 'Test.Program.DataClass' to
// 'System.IComparable<Test.Program.DataClass>'.
然后实现接口将解决编译错误:
public class DataClass : IComparable<DataClass>
{
public int Value { get; set; }
public DataClass(int value)
{
Value = value;
}
public int CompareTo(DataClass other)
{
return other == null ? 1 : Value.CompareTo(other.Value);
}
}