英文:
How to sort a generic class?
问题
如何对一个包含字符串和整数的类的列表进行排序。
编辑:我已经简化了查询代码。results2中的ToArray()方法生成了错误。为什么会这样?
var results1 = Entities.OrderBy(x => ((Cell<object>)x.RowData[CurrentPlugin.SortOrderInfo.ColumnIndex]).Value);
var results2 = Entities.OrderBy(x => ((Cell<object>)x.RowData[CurrentPlugin.SortOrderInfo.ColumnIndex]).Value).ToArray();
System.InvalidCastException: '无法将类型为 'Cell
1[System.String]' 的对象强制转换为类型 'Cell
1[System.Object]'。'
public class Cell<T> : IComparable<Cell<T>>
{
public Cell()
{
Text = string.Empty;
Value = default;
}
public Cell(int index)
{
Index = index;
Text = string.Empty;
Value = default;
}
public Cell(string text)
{
Text = text;
Value = default;
}
public Cell(string text, T value)
{
Text = text;
Value = value;
}
public int Index { get; set; }
public string Text { get; set; }
public T Value { get; set; }
public int CompareTo(Cell<T>? other)
{
if (other == null)
return 1;
// 根据值的类型进行比较
if (Value is IComparable<T> comparableValue)
return comparableValue.CompareTo(other.Value);
// 如果值的类型不可比较,只比较文本值
return Text.CompareTo(other.Text);
}
public override string ToString()
{
return Text;
}
}
英文:
How can I sort a list of this class where T in the list can be string and integers.
Edit: I have simplified the query code. The ToArray() method in results2 generates the error. Why is that?
var results1 = Entities.OrderBy(x => ((Cell<object>)x.RowData[CurrentPlugin.SortOrderInfo.ColumnIndex]).Value);
var results2 = Entities.OrderBy(x => ((Cell<object>)x.RowData[CurrentPlugin.SortOrderInfo.ColumnIndex]).Value).ToArray();
> System.InvalidCastException: 'Unable to cast object of type 'Cell1[System.String]' to type 'Cell
1[System.Object]'.'
public class Cell<T> : IComparable<Cell<T>>
{
public Cell()
{
Text = string.Empty;
Value = default;
}
public Cell(int index)
{
Index = index;
Text = string.Empty;
Value = default;
}
public Cell(string text)
{
Text = text;
Value = default;
}
public Cell(string text, T value)
{
Text = text;
Value = value;
}
public int Index { get; set; }
public string Text { get; set; }
public T Value { get; set; }
public int CompareTo(Cell<T>? other)
{
if (other == null)
return 1;
// Compare based on the type of the values
if (Value is IComparable<T> comparableValue)
return comparableValue.CompareTo(other.Value);
// If the Value type is not comparable, just compare the Text values
return Text.CompareTo(other.Text);
}
public override string ToString()
{
return Text;
}
}
答案1
得分: 4
以下是代码的翻译部分:
.ThenBy(x => ((Cell<object>)x.RowData[CurrentPlugin.SortOrderInfo.ColumnIndex]).Value))
这段代码无法工作,因为Cell<T>
不等于Cell<object>
,除非T
等于object
(例如:https://stackoverflow.com/q/2033912/2501279)。即使你引入了协变接口ICell<out T>
,对于int
类型也无法工作,因为协变性只支持引用类型。
解决方法:
-
根据实际类型进行比较:
如果你知道列表既可以包含整数又可以包含字符串,你可以这样做:
x.OrderBy(o => o switch { Cell<int> ints => ints.Value.ToString(), Cell<string> strings => strings.Value, _ => null }) .ToList();
如果你知道数据是同质的,你可以使用装箱的方法:
x.OrderBy(o => o switch { Cell<int> ints => (object)ints.Value, Cell<string> strings => strings.Value, _ => null }) .ToList();
-
实现非泛型的
IComparable
接口(不确定如何实现),然后进行类型转换。 -
对于同质的情况,你可以创建非泛型接口:
interface ICell { public object Value { get; } }
并且以显式方式实现它,并进行类型转换。
-
实现
IComparer<Entity>
:class StringOrIntCellComparer : IComparer<Entity> { public int Compare(Entity? left, Entity? right) { var x = left.RowData[CurrentPlugin.SortOrderInfo.ColumnIndex]; var y = right.RowData[CurrentPlugin.SortOrderInfo.ColumnIndex]; if (x is Cell<int> xi && y is Cell<int> yi) { return xi.Value.CompareTo(yi.Value); // or call compare from class } if (x is Cell<string> xs && y is Cell<string> ys) { return xs.Value.CompareTo(ys.Value); // or call compare from class } // todo: type mismatch or unsupported type throw new Exception(); } }
并在
OrderBy
中使用它 -Entities.OrderBy(x => x, new StringOrIntCellComparer())
但可能你需要完全重新设计你的代码。
附注:
在
results2
中使用ToArray()
方法会生成错误
这是因为LINQ是延迟执行的,result1
并没有实际执行过滤操作,而ToArray
是一种实现操作符,会导致实际执行。
英文:
The following code:
.ThenBy(x => ((Cell<object>)x.RowData[CurrentPlugin.SortOrderInfo.ColumnIndex]).Value))
Will not work because Cell<T>
is not Cell<object>
unless T
== object
(for example - https://stackoverflow.com/q/2033912/2501279 ). Even if you will introduce covariant interface ICell<out T>
it still will not work for int
's because variance is supported only for reference types.
Workarounds:
-
Compare against actual type:
If you know that list can contain both ints and strings then you can do something like:
x.OrderBy(o => o switch { Cell<int> ints => ints.Value.ToString(), Cell<string> strings => strings.Value, _ => null }) .ToList();
If you know that data is homogeneous then you can go with boxing approach:
x.OrderBy(o => o switch { Cell<int> ints => (object)ints.Value, Cell<string> strings => strings.Value, _ => null }) .ToList();
-
Implement non-generic
IComparable
(not sure how though) and cast to it. -
In case of homogeneous you can create non-generic interface:
interface ICell { public object Value { get; } }
and implement it explicitly and cast to it.
-
Implement
IComparer<Entity>
:class StringOrIntCellComparer : IComparer<Entity> { public int Compare(Entity? left, Entity? right) { var x = left.RowData[CurrentPlugin.SortOrderInfo.ColumnIndex]; var y = right.RowData[CurrentPlugin.SortOrderInfo.ColumnIndex]; if (x is Cell<int> xi && y is Cell<int> yi) { return xi.Value.CompareTo(yi.Value); // or call compare from class } if (x is Cell<string> xs && y is Cell<string> ys) { return xs.Value.CompareTo(ys.Value); // or call compare from class } // todo: type mismatch or unsupported type throw new Exception(); } }
and use in in the
OrderBy
-Entities.OrderBy(x => x, new StringOrIntCellComparer())
But possibly you need to rework your design completely.
P.S.
> The ToArray() method in results2 generates the error
Because LINQ is lazy and result1
does not actually perform filtering while ToArray
is one of the materialization operators which results in actual execution.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论