LINQ OrderBy 索引;使用通用 TKey 出错
LINQ OrderBy with index ; getting error using Generic TKey
我正在尝试根据所选 属性:
的索引订购 IEnumerable<MyClass>
class MyClass
{
public string Name;
public string Value;
}
我有一个辅助方法来获取 Expression<>
的条件。
它被编译以创建 Func<>
来帮助排序。
我在尝试使 Func<>
return 成为要订购的通用 TKey
时遇到问题。
public Expression<Func<T, TKey>> Helper<T>(int propIdx)
{
var param = Expression.Parameter(typeof(T), "record");
var props = typeof(T).GetProperties();
if(propIdx > props.Count())
return Expression.Lambda<Func<T, TKey>>)(Expression.Constant(true), param);
Expression propExp = Expression.Property(param, props[propIdx].Name);
return lambda = Expression.Lambda<Func<T, TKey>>(propExp, param);
}
这用于帮助动态订单标准。
OrderByIdx(IEnumerable<MyClass> input, int propIdx)
{
Expression<Func<T, TKey>> exp = Helper(propIdx);
var funct = exp.Compile();
return input.OrderBy(funct);
}
我遇到错误 The type or namespace TKey could not be found
如何使用 Generic<TKey>
来帮助订购?
要能够在 属性 上订购,它必须是实现 IComparable
的类型。您可以使用此事实在您的代码中将 TKey
替换为 IComparable
:
class MyClass
{
public string Name { get; set; }
public string Value { get; set; }
}
public Expression<Func<T, IComparable>> Helper<T>(int propIdx)
{
var param = Expression.Parameter(typeof(T), "record");
var props = typeof(T).GetProperties();
Expression propExp = Expression.Property(param, props[propIdx].Name);
return Expression.Lambda<Func<T, IComparable>>(propExp, param);
}
IEnumerable<MyClass> OrderByIdx(IEnumerable<MyClass> input, int propIdx)
{
Expression<Func<MyClass, IComparable>> exp = Helper<MyClass>(propIdx);
var funct = exp.Compile();
return input.OrderBy(funct);
}
首先:Type.GetProperties
没有明确的顺序。
var props = typeof(T).GetProperties();
哪个 属性 会在 props[0]
中?所有属性都可读吗?
因此,如果您想获得定义的顺序,我的建议是进行一些排序。例如获取按名称排序的 public 可读属性。
此外,如果您的代码的用户(= 软件,而不是操作员)想要使用基于索引的通用代码,这不是有点奇怪吗?
- 他们有 class
MyClass
和 属性 Date
- 他们有这个 class 的对象序列。
- 他们想在 属性
Date
之前订购
- 不是按 属性
Date
或按 属性 命名为“日期”的顺序,而是必须确定 属性 Date
的索引:日期好像是第四个属性
- 然后他们必须使用此索引调用您的方法:“OrderBy 索引 4”
如果他们可以直接说:“按 属性 Date
订购”或“按名为 'Date' 的 属性 订购,岂不是更容易? ?
IEnumerable<MyClass> source = ...
var orderedSource = source.OrderBy(t => t.Date);
有时您的用户无法访问 属性,他们只知道 属性 的名称。在这种情况下,您可以创建一个扩展方法,在其中提供 属性 的名称,并且 returns 订购的源。
如果您不熟悉扩展方法。参见 extension methods demystified
用法类似于:
var orderedSource = source.OrderBy(nameof(MyClass.Date));
或者例如:“按我的 DataGridView 中选定的列排序”。
string propertyName = this.DataGridView.Columns.Cast<DataGridViewColumn>()
.Where(column => column.IsSelected)
.Select(column => column.DataPropertyName)
.FirstOrDefault();
var orderedSource = source.OrderBy(propertyName);
这样的程序容易制作,易于使用和重用,易于测试,易于维护。最重要的是:他们是单线的:
public static IOrderedEnumerable<T> OrderBy<T>(
this IEnumerable<T> source,
string propertyName)
{
// TODO: handle invalid input
PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName);
// TODO: handle invalid propertyname
return source.OrderBy(propertyInfo);
}
public static IOrderedEnumerable<T> OrderBy<T>(
this IEnumerable<T> source,
PropertyInfo propertyInfo)
{
// TODO: handle invalid parameters
return source.OrderBy(t => propertyInfo.GetValue(t) as propertyInfo.PropertyType);
}
如果需要,您可以为 ThenBy(this IEnumerable<T>, ...)
.
创建重载
但我想按索引排序,而不是按名称排序!
如果您真的想按索引排序,使用 Lambda 表达式创建扩展方法更容易,然后 fiddle 使用表达式。
考虑以下扩展方法:
public static IOrderedEnumerable<T> OrderBy<T>(
IEnumerable<T> source,
int propertyIndex)
{
return source.OrderBy(propertyIndex, GetDefaultPropertyOrder(typeof(T));
}
public static IOrderedEnumerable<T> OrderBy<T>(
IEnumerable<T> source,
int propertyIndex,
IReadOnlyList<PropertyInfo> properties)
{
// TODO: handle null and out-of-range parameters
PropertyInfo sortProperty = properties[propertyIndex];
return source.OrderBy(sortProperty);
}
public static IList<PropertyInfo> GetDefaultPropertyOrder(Type t)
{
return t.GetProperties()
.Where(property => property.CanRead)
.OrderBy(property => property.Name)
.ToList();
}
用法:
BindingList<MyClass> myObjects = ...
// display myObjects in a dataGridView
this.DataGridView.DataSource = myObjects;
// event if operator clicks on column header:
public void OnColumnHeaderClicked(object sender, ...)
{
DataGridViewColumn clickedColumn = GetClickedColumn();
// sort by column index, as you prefer:
var sortedObjects = myObjects.SortBy(clickedColumn.DisplayIndex);
ProcessSortedObjects(sortedObjects);
// but of course, you skip some intermediate methods if you sort by property name
var sortedObject = myObject.SortBy(clickedColumn.DataPropertyName);
ProcessSortedObjects(sortedObjects);
}
我正在尝试根据所选 属性:
的索引订购IEnumerable<MyClass>
class MyClass
{
public string Name;
public string Value;
}
我有一个辅助方法来获取 Expression<>
的条件。
它被编译以创建 Func<>
来帮助排序。
我在尝试使 Func<>
return 成为要订购的通用 TKey
时遇到问题。
public Expression<Func<T, TKey>> Helper<T>(int propIdx)
{
var param = Expression.Parameter(typeof(T), "record");
var props = typeof(T).GetProperties();
if(propIdx > props.Count())
return Expression.Lambda<Func<T, TKey>>)(Expression.Constant(true), param);
Expression propExp = Expression.Property(param, props[propIdx].Name);
return lambda = Expression.Lambda<Func<T, TKey>>(propExp, param);
}
这用于帮助动态订单标准。
OrderByIdx(IEnumerable<MyClass> input, int propIdx)
{
Expression<Func<T, TKey>> exp = Helper(propIdx);
var funct = exp.Compile();
return input.OrderBy(funct);
}
我遇到错误 The type or namespace TKey could not be found
如何使用 Generic<TKey>
来帮助订购?
要能够在 属性 上订购,它必须是实现 IComparable
的类型。您可以使用此事实在您的代码中将 TKey
替换为 IComparable
:
class MyClass
{
public string Name { get; set; }
public string Value { get; set; }
}
public Expression<Func<T, IComparable>> Helper<T>(int propIdx)
{
var param = Expression.Parameter(typeof(T), "record");
var props = typeof(T).GetProperties();
Expression propExp = Expression.Property(param, props[propIdx].Name);
return Expression.Lambda<Func<T, IComparable>>(propExp, param);
}
IEnumerable<MyClass> OrderByIdx(IEnumerable<MyClass> input, int propIdx)
{
Expression<Func<MyClass, IComparable>> exp = Helper<MyClass>(propIdx);
var funct = exp.Compile();
return input.OrderBy(funct);
}
首先:Type.GetProperties
没有明确的顺序。
var props = typeof(T).GetProperties();
哪个 属性 会在 props[0]
中?所有属性都可读吗?
因此,如果您想获得定义的顺序,我的建议是进行一些排序。例如获取按名称排序的 public 可读属性。
此外,如果您的代码的用户(= 软件,而不是操作员)想要使用基于索引的通用代码,这不是有点奇怪吗?
- 他们有 class
MyClass
和 属性Date
- 他们有这个 class 的对象序列。
- 他们想在 属性
Date
之前订购
- 不是按 属性
Date
或按 属性 命名为“日期”的顺序,而是必须确定 属性Date
的索引:日期好像是第四个属性 - 然后他们必须使用此索引调用您的方法:“OrderBy 索引 4”
如果他们可以直接说:“按 属性 Date
订购”或“按名为 'Date' 的 属性 订购,岂不是更容易? ?
IEnumerable<MyClass> source = ...
var orderedSource = source.OrderBy(t => t.Date);
有时您的用户无法访问 属性,他们只知道 属性 的名称。在这种情况下,您可以创建一个扩展方法,在其中提供 属性 的名称,并且 returns 订购的源。
如果您不熟悉扩展方法。参见 extension methods demystified
用法类似于:
var orderedSource = source.OrderBy(nameof(MyClass.Date));
或者例如:“按我的 DataGridView 中选定的列排序”。
string propertyName = this.DataGridView.Columns.Cast<DataGridViewColumn>()
.Where(column => column.IsSelected)
.Select(column => column.DataPropertyName)
.FirstOrDefault();
var orderedSource = source.OrderBy(propertyName);
这样的程序容易制作,易于使用和重用,易于测试,易于维护。最重要的是:他们是单线的:
public static IOrderedEnumerable<T> OrderBy<T>(
this IEnumerable<T> source,
string propertyName)
{
// TODO: handle invalid input
PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName);
// TODO: handle invalid propertyname
return source.OrderBy(propertyInfo);
}
public static IOrderedEnumerable<T> OrderBy<T>(
this IEnumerable<T> source,
PropertyInfo propertyInfo)
{
// TODO: handle invalid parameters
return source.OrderBy(t => propertyInfo.GetValue(t) as propertyInfo.PropertyType);
}
如果需要,您可以为 ThenBy(this IEnumerable<T>, ...)
.
但我想按索引排序,而不是按名称排序!
如果您真的想按索引排序,使用 Lambda 表达式创建扩展方法更容易,然后 fiddle 使用表达式。
考虑以下扩展方法:
public static IOrderedEnumerable<T> OrderBy<T>(
IEnumerable<T> source,
int propertyIndex)
{
return source.OrderBy(propertyIndex, GetDefaultPropertyOrder(typeof(T));
}
public static IOrderedEnumerable<T> OrderBy<T>(
IEnumerable<T> source,
int propertyIndex,
IReadOnlyList<PropertyInfo> properties)
{
// TODO: handle null and out-of-range parameters
PropertyInfo sortProperty = properties[propertyIndex];
return source.OrderBy(sortProperty);
}
public static IList<PropertyInfo> GetDefaultPropertyOrder(Type t)
{
return t.GetProperties()
.Where(property => property.CanRead)
.OrderBy(property => property.Name)
.ToList();
}
用法:
BindingList<MyClass> myObjects = ...
// display myObjects in a dataGridView
this.DataGridView.DataSource = myObjects;
// event if operator clicks on column header:
public void OnColumnHeaderClicked(object sender, ...)
{
DataGridViewColumn clickedColumn = GetClickedColumn();
// sort by column index, as you prefer:
var sortedObjects = myObjects.SortBy(clickedColumn.DisplayIndex);
ProcessSortedObjects(sortedObjects);
// but of course, you skip some intermediate methods if you sort by property name
var sortedObject = myObject.SortBy(clickedColumn.DataPropertyName);
ProcessSortedObjects(sortedObjects);
}