Как заказать коллекцию на основе дочернего свойства, используя LINQ или Lambda? - PullRequest
1 голос
/ 26 января 2012

Я получаю следующее string выражение:

"ChildObject.FullName" ... где ChildObject - это свойство экземпляра для типа MyObject1.

ChildObject имеет свойство с именем «FullName», и я хочу отсортировать коллекцию типа «MyObject1» на основе значения этого «дочернего свойства» «FullName».

Я могу делать это целый день со свойствами непосредственно на MyObject1, но я сталкиваюсь с 2 вызовами, когда выполняю это на дочернем экземпляре, и я не могу заставить все части работать. Основные 2 вызова:

  1. MyObject1 имеет несколько различных типов дочерних свойств, поэтому я не могу жестко закодировать тип для ChildObject. строка может быть любого типа.
  2. Выражение сортировки - это строка, а не известный тип.

Для # 2 выше я могу использовать Reflection, чтобы получить тип дочернего свойства, но я просто не могу заставить все это работать. У меня есть следующее ниже, и он компилируется и запускается, но не сортирует по-другому:

'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, действительно предоставляет мне необходимую информацию 'FullName'. Поэтому код должен быть близким, но это все еще не работает.

Есть идеи, как мне это сделать? То, что я пытаюсь предотвратить, это жесткое кодирование серии блоков «If» для дочернего типа, чтобы затем жестко кодировать его тип в метод sort или OrderBy.

Спасибо!

Ответы [ 2 ]

2 голосов
/ 27 января 2012

Если я правильно понимаю вопрос (отказ от ответственности - это первый код на vb.net, который я когда-либо писал, он может быть не самым лучшим, синтаксически - я написал его сначала на c #), один из способов добиться этого - делаем следующее ...

Допустим, ваш MyObject1 выглядит следующим образом:

Public Class MyObject1

    Private mChildObject As SortableChildObject

    Public Property ChildObject() As SortableChildObject
        Get
            ChildObject = mChildObject
        End Get
        Set(value As SortableChildObject)
            mChildObject = value
        End Set
    End Property
End Class

Обратите внимание, что у него есть свойство, которое должно быть "SortableChildObject" - этот класс выглядит следующим образом:

' Implement IComparable using reflection - just look up the property to
' sort on based on the "SortExpression" property 
Public MustInherit Class SortableChildObject
    Implements IComparable

    Protected MustOverride ReadOnly Property SortExpression() As String

    Public Function CompareTo(obj As Object) As Integer Implements System.IComparable.CompareTo

        ' Make sure the object we are comparing to is also our type
        Dim oo As SortableChildObject = TryCast(obj, SortableChildObject)
        If oo Is Nothing Then
            Throw New ArgumentException("I cannot compare these two objects")
        End If

        ' Get the value to sort on for this object
        Dim thisVal As IComparable = GetSortableValue(Me, SortExpression)
        If thisVal Is Nothing Then
            Throw New ArgumentException("Could not get the value of the sortable property for this")
        End If

        ' Get the value to sort on for the object we are comparing to
        Dim thatVal As IComparable = GetSortableValue(oo, oo.SortExpression)
        If thatVal Is Nothing Then
            Throw New ArgumentException("Could not get the value of the sortable property for that")
        End If

        ' Use the IComparable implementation of the properties we are comparing
        Return thisVal.CompareTo(thatVal)
    End Function

    Private Function GetSortableValue(obj As Object, sortExpression As String) As IComparable

        Dim prop As PropertyInfo = obj.GetType().GetProperty(sortExpression)
        If prop Is Nothing Then
            Throw New ArgumentException("Could not find the property " + sortExpression)
        End If

        Dim val As Object = prop.GetValue(obj, Nothing)

        Dim ret As IComparable = TryCast(val, IComparable)
        If ret Is Nothing Then
            Throw New ArgumentException("No way to compare the values as the comparable property does not implement IComparable")
        End If

        Return ret
    End Function
End Class

Теперь вам нужно убедиться, что все вещи, которые вы хотите отсортировать, наследуют от этого класса, например, говорят, что у нас есть что-то, у которого есть свойство FullName String, это будет выглядеть так:

' This is a child object that has a string property called "FullName" which
' is what we want to sort on
Public Class FullNameChildObject
    Inherits SortableChildObject

    Private mFullName As String

    Protected Overrides ReadOnly Property SortExpression() As String
        Get
            SortExpression = "FullName"
        End Get
    End Property

    Public Property FullName() As String
        Get
            FullName = mFullName
        End Get
        Set(value As String)
            mFullName = value
        End Set
    End Property

End Class

Итак, чтобы использовать это, давайте создадим небольшой список объектов для сортировки следующим образом:

Dim myObject1List As New List(Of MyObject1)

Dim i As FullNameChildObject = New FullNameChildObject
i.FullName = "B"

Dim o As New MyObject1
o.ChildObject = i
myObject1List.Add(o)

i = New FullNameChildObject
i.FullName = "A"
o = New MyObject1
o.ChildObject = i
myObject1List.Add(o)

i = New FullNameChildObject
i.FullName = "D"
o = New MyObject1
o.ChildObject = i
myObject1List.Add(o)

i = New FullNameChildObject
i.FullName = "C"
o = New MyObject1
o.ChildObject = i
myObject1List.Add(o)

В этом случае сортировка на основе свойства ChildObject очень проста, все, что вам нужно сделать, это:

Dim ret = myObject1List.OrderBy(Function(x)
                                    Return x.ChildObject
                                End Function)

Это то, что вам нужно? Это немного проблематично сделать таким образом - как вы видите, во многих местах что-то может пойти не так - если, например, вы смешиваете объекты, которые хотите сравнить (скажем, у вас есть один, который сортирует по целому числу и еще один за строкой) он выдаст исключение.

1 голос
/ 27 января 2012

Если я правильно понимаю, у вас есть объект (скажем, родитель), который содержит объект (скажем, ребенок). У ребенка есть поле FullName, и вы хотите отсортировать список родителей по ребенку FullName.

Если это так, то OrderBy () должно сделать это за вас.

Скажем, у нас есть список родителей

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 ()

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

дает

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"}}

(пример ниже)

чч,
Алан.

Редактировать

Просто перечитайте вопрос. У вас есть различные свойства FullName , которые выбираются по имени (из строки запроса?)

Вы можете выбрать свойство по имени, используя выражение (см. Как мне создать динамический Select для IEnumerable во время выполнения? ) для общей фигуры.

Если выбранное свойство является IComparable (или оно IEquatable? Не слишком уверен, какое именно), то OrderBy () все равно будет работать. Это означает, что пока поля сортировки являются базовыми типами, у вас все в порядке. Если они являются пользовательскими типами (объектами), вам необходимо проделать дополнительную работу ...

Извините за первый неправильный ответ.

Больше правок

У нас пятница и медленно:?

ОК, расширен ответ для доступа к различным дочерним элементам по имени. (Я использовал поля, а не свойства, но любой из них будет работать). Нам все еще нужно знать тип поля, но немного больше работы может удалить это (при необходимости).

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...