如何使用LINQ或Lambda基于子属性订购集合? [英] How do I order a collection based on a child property using LINQ or a Lambda?

查看:55
本文介绍了如何使用LINQ或Lambda基于子属性订购集合?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我提供了以下 string 表达式:

"ChildObject.FullName" ...其中,ChildObject是MyObject1类型的实例属性.

"ChildObject.FullName" ...where ChildObject is an instance property on the MyObject1 type.

ChildObject有一个名为"FullName"的属性,我想根据此子属性"FullName"的值对类型为"MyObject1"的集合进行排序.

ChildObject has a property named "FullName" and I want to sort a collection of type "MyObject1" based on this child properties "FullName" value.

我可以整天直接在MyObject1上的属性上执行此操作,但是在子实例上执行该操作时会遇到2个挑战,而我无法解决所有问题.主要的2个挑战是:

I can do this all day long on properties directly on MyObject1 but I run into 2 challanges when doing it on a child instance and I can't get all the pieces working. The main 2 challanges are:

  1. MyObject1有几种不同的子属性类型,因此我无法对ChildObject的类型进行硬编码. string 可以是任何类型.
  2. 排序表达式是String而不是已知类型.
  1. MyObject1 has a few different child property types so I can't hardcode the type for ChildObject. The string could be of any type.
  2. The sort expression is a String and not a known type.

对于上面的#2,我可以使用Reflection来获取child属性的类型,但是我不能全部使用它.我在下面看到以下内容,并且可以编译并运行,但是排序没有任何不同:

For #2 above I can use Reflection to get the type of the child property, but I just can't get it all to work. I have the following below and it compiles and runs, but does not sort any differently:

'SortExpression below is a String like: "ChildObject.FullName"
MyObject1List = MyObject1List.OrderBy(Function(x)
      Dim t As Type = x.GetType()
      Dim tp As Type = t.GetProperty(SortExpression.Split(".").ElementAt(0)).PropertyType()
      Return tp.GetProperty(Request.CompareExpression.Split(".").ElementAt(1))
      End Function).ToList()

在表达式的最后一行返回的值上方(如果我运行代码超出OrderBy方法,则确实为我提供所需的全名"信息.因此,代码必须关闭,但它仍然无法正常工作.

Above the value returned from the last line in the expression (if I run the code outsode the OrderBy method, does provide me the 'FullName' information I need. So the code must be close, but it still does not work.

关于如何实现此目标的任何想法?我要防止的是对孩子的类型进行硬编码一系列"If"块,然后将其类型硬编码为sort或OrderBy方法.

Any ideas on how I can accomplish this? What I am trying to prevent is hardcoding a series of 'If' blocks on the child's type to then hardcode in its type to the sort or OrderBy method.

谢谢!

推荐答案

如果我正确理解这一点,则您有一个包含对象(例如Child)的对象(例如Parent).子级有一个全名字段,您想按子级全名对父母列表进行排序.

If I understand this correctly you have an object (say, Parent) which contains an object (say, Child). Child has a field FullName and you want to sort a list of parents by the child FullName.

如果是这样,则 OrderBy()应该为您完成.

If so, then then OrderBy() should do it for you.

说我们有父母名单

父母{ID = 1,孩子= {ID = 1,全名="Jan"}} 父母{Id = 2,孩子= {Id = 2,全名="Feb"}} 父母{ID = 3,孩子= {ID = 3,全名="Mar"}} 父母{ID = 4,孩子= {ID = 4,全名="Apr"}}

Parent{ Id = 1, Child = { Id = 1, FullName = "Jan"}} Parent{ Id = 2, Child = { Id = 2, FullName = "Feb"}} Parent{ Id = 3, Child = { Id = 3, FullName = "Mar"}} Parent{ Id = 4, Child = { Id = 4, FullName = "Apr"}}

使用OrderBy()对它们进行排序

sorting them using OrderBy()

Dim sorted = Items.OrderBy(Function(itm) itm.Child.FullName)

给予

父母{ID = 4,孩子= {ID = 4,全名="Apr"}} 父母{Id = 2,孩子= {Id = 2,全名="Feb"}} 父母{Id = 1,孩子= {Id = 1,全名="Jan"}} 父母{ID = 3,孩子= {ID = 3,全名="Mar"}}

Parent{ Id = 4, Child = { Id = 4, FullName = "Apr"}} Parent{ Id = 2, Child = { Id = 2, FullName = "Feb"}} Parent{ Id = 1, Child = { Id = 1, FullName = "Jan"}} Parent{ Id = 3, Child = { Id = 3, FullName = "Mar"}}

(下面的示例)

hth,
艾伦.

hth,
Alan.

修改

只需重新阅读问题即可.您有不同的 FullName 属性,这些属性是通过名称(从查询字符串中选择的)来选择的.

Just re-read the question. You have different FullName properties which are selected by name (from a query string?)

您可以使用表达式按名称选择属性(请参见

You can select the property by name using an expression (see, How can I create a dynamic Select on an IEnumerable<T> at runtime?) for the general shape.

如果所选属性是IComparable(或者它是IEquatable的?不是很确定哪个),那么OrderBy()仍然可以工作.这意味着只要排序字段是基本类型,就可以了.如果它们是自定义类型(对象),则您需要做更多的工作...

If the selected property is IComparable (Or is it IEquatable? Not too sure which) then the OrderBy() will still work. This means that as long as the sort fields are basic types you are fine. If they are custom types (objects) you will need to do some more work...

对不起,第一个错误答案.

Sorry about the first mis-answer.

更多修改

今天是星期五,这里很慢:?

It's Friday and slow in here :?

确定,扩展答案以按名称访问不同的子成员. (我使用的是字段而不是属性,但是两者都可以).我们仍然需要知道字段的类型,但是如果需要的话,更多的工作可能会删除它.

OK, expanded the answer to access different child memebers by name. (I used Fields rather than Properties but either will work). We still need to know the type of the field but a bit more work might remove that (if needed).

Private Class Child
Public Id As Integer
Public FullName As String
End Class

Private Class Parent
Public Id As Integer
Public Child As Child
End Class

Private Items As New List(Of Parent)() From { _
New Parent() With { _
    Key .Id = 1, _
    Key .Child = New Child() With { _
        Key .Id = 1, _
        Key .FullName = "Jan" _
    } _
}, _
New Parent() With { _
    Key .Id = 2, _
    Key .Child = New Child() With { _
        Key .Id = 2, _
        Key .FullName = "Feb" _
    } _
}, _
New Parent() With { _
    Key .Id = 3, _
    Key .Child = New Child() With { _
        Key .Id = 3, _
        Key .FullName = "Mar" _
    } _
}, _
New Parent() With { _
    Key .Id = 4, _
    Key .Child = New Child() With { _
        Key .Id = 4, _
        Key .FullName = "Apr" _
    } _
} _
}

<TestMethod> _
Public Sub SortByChildName()
Dim expectedParentIds = New () {4, 2, 1, 3}
Dim sortedIds = Items.OrderBy(SelectExpression(Of Parent, String)("Child.FullName")).[Select](Function(itm) itm.Id)

Assert.IsTrue(expectedParentIds.SequenceEqual(sortedIds))
End Sub

<TestMethod> _
Public Sub SortByChildId()

Dim expectedParentIds = New () {4, 3, 2, 1}
Dim sortedIds = Items.OrderBy(SelectExpression(Of Parent, Integer)("Child.Id")).[Select](Function(itm) itm.Id)

Assert.IsTrue(expectedParentIds.SequenceEqual(sortedIds))

End Sub


Public Shared Function SelectExpression(Of TItem, TField)(fieldNames As String) As Func(Of TItem, TField)

Dim type = GetType(TItem)
Dim fields = fieldNames.Split("."C)

Dim arg As ParameterExpression = Expression.Parameter(type, "item")
Dim expr As Expression = arg

For Each field As String In fields
    Dim fieldInfo = type.GetField(field)
    expr = Expression.Field(expr, fieldInfo)
    type = fieldInfo.FieldType
Next

Return Expression.Lambda(Of Func(Of TItem, TField))(expr, arg).Compile()

End Function

这篇关于如何使用LINQ或Lambda基于子属性订购集合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆