2017-02-13 14 views
-1

我试图让generic method从下面的代码段延长Array通用函数数组转换为KeyValuePair的排序列表(downvoted)

Public Class clsField 
    Public idx As String 
    Public name As String 
    Public weight As Long 

    Public Sub New(i As String, n As String, w As Long) 
     idx = i : name = n : weight = w 
    End Sub 
End Class 

Public Class Container 
    Public fields As clsField() ' filled in by a JSON parser (order matters) 

    ' returns a list sorted by clsField.weight preserving order for elements with same 'weight' value 
    Public Function getFields() As List(Of KeyValuePair(Of String, clsField)) 
     Dim auxList As List(Of KeyValuePair(Of String, clsField)) 

     If (fields Is Nothing) OrElse (fields.Count < 1) Then Return New List(Of KeyValuePair(Of String, clsField)) 
     ' .ToList to transform IEnumerable to the return type 
     auxList = Array.ConvertAll(fields, New Converter(Of clsField, KeyValuePair(Of String, clsField))(AddressOf FieldToPair)).ToList 
     Return auxList.OrderBy(Function(x) x.Value.weight).ToList() 
    End Function 

    Public Shared Function FieldToPair(fld As clsField) As KeyValuePair(Of String, clsField) 
     Return New KeyValuePair(Of String, clsField)(fld.idx, fld) 
    End Function 
End Class 

我坚持的Converter(Of TInput, TOutput) Delegate,使用Array.ConvertAll ,这将不接受新的参数,前提是我可以通过一个函数来指定,应该在TInput使用的key

Private Function ClassToPair(Of T)(obj As T, getProperty As Func(Of T, Object)) As KeyValuePair(Of String, T) 
    Return New KeyValuePair(Of String, T)(getProperty(obj), obj) 
End Function 

或许有办法OverloadArray.ConvertAll并创建一个替代DelegateConverter,签名允许完成下面的代码(显然不编译为ConvertAllAddressOf ClassToPair;这里为了反映的想法):

Module ArrayExtension ' custom method for array 
    ' returns a list sorted by clsField.weight preserving order for elements with same 'weight' value 
    ' getKey is used to transform the array into a List (Of KeyValuePair (Of String, T)) -> using the Converter 
    ' getSortProperty is used to change the sorting 'property' 
    <Extension()> 
    Public Function toSortedPairedList(Of T)(arr As T(), Optional getKey As Func(Of T, String) = Nothing, 
      Optional getSortProperty As Func(Of KeyValuePair(Of String, T), Object) = Nothing) _ 
      As List(Of KeyValuePair(Of String, T)) 
     Dim auxList As List(Of KeyValuePair(Of String, T)) 

     If (arr Is Nothing) OrElse (arr.Count < 1) Then Return New List(Of KeyValuePair(Of String, T)) 

     ' .ToList to transform IEnumerable to the return type 
     auxList = Array.ConvertAll(arr, New Converter(Of T, KeyValuePair(Of String, T))(AddressOf ClassToPair)).ToList 
     Return auxList.OrderBy(getSortProperty).ToList() 

    End Function 

    Private Function ClassToPair(Of T)(obj As T, getProperty As Func(Of T, Object)) As KeyValuePair(Of String, T) 
     Return New KeyValuePair(Of String, T)(getProperty(obj), obj) 
    End Function 
End Module 

所以,没办法getKey功能传递到转换器 ...

对于它的使用会像第一个例子:

Public Function getFields() As List(Of KeyValuePair(Of String, clsField)) 
    Dim auxList As List(Of KeyValuePair(Of String, clsField)) 

    If (fields Is Nothing) OrElse (fields.Count < 1) Then Return New List(Of KeyValuePair(Of String, clsField)) 

    Return fields.toSortedPairedList(Function(x) x.idx, Function(y) y.Value.weight) 
End Function 
+0

如果没有**存储** Func'' getProperty'来访问包含对象'T' [...'array()as T']的属性,那么这是不可能的,它告诉哪个是'TKey'在转换过程中创建'KeyValuePair' ...因此实现转换的函数应该首先将'getProperty'存储在一个变量中,该变量可以被**访问**并从'ClassToPair'中调用。所以说它应该实现两次:** 1。** store'getProperty',** 2。**'Array.ConvertAll' ... – rellampec

回答

0

我会为我的问题提供一个答案。

虽然我不喜欢这种方法的清洁的原因(即该方法的类型参数太多),但它似乎是通过使用Extension Methods来解决它的唯一方法。

它具有使得能够使用的Converter,围绕该Delegate签名引入了限制工作,通过存储KeySelector成通用的,模块变量的优点:Private accessKey As Object

我的猜测是最好的方法是实现一个完全泛型类,它不仅仅依赖于KeyValuePairs,而是具有更广泛的转换。

首先,在存储KeySelector的目标,这将有助于一个List(Of TValue)ConvertAll一个List(Of KeyValuePair(Of TKey, TValue)),泛型类是必要的,这样我们就可以存储与没有编译错误一generic lambdareference)(似乎有不是有更好的方式来解决该限制的Converter Delegate介绍):

' to store a generic lambda: https://stackoverflow.com/a/3116009/6215377 
' the class shows up to be necessary, as it cannot be stored by the use of generic delegates 
' otherwise, you will always end up having to instantiate to specific types for KeyValuePair, 
' losing so the generic declaration 
Interface IAccesser(Of TValue, TKey) 
    Function accessProperty(input As TValue) As TKey 
End Interface 

Public Class PropertyAccessor(Of TValue, TKey) 
    Implements IAccesser(Of TValue, TKey) 
    Private pFunc As Func(Of TValue, TKey) 

    Public Sub New(f As Func(Of TValue, TKey)) 
     pFunc = f 
    End Sub 
    Function accessProperty(o As TValue) As TKey Implements IAccesser(Of TValue, TKey).accessProperty 
     Return pFunc(o) 
    End Function 
End Class 

现在Extension Module重新调整,前提是getKey功能参数传递给toSortedPairedList存储到generic模块的对象accessKey As Object,在致电ConvertAllClassToPairConverter(将使用accessKey的那个)之前。这需要从Object一些type castings到类PropertyAccessor

Imports System.Linq.Enumerable 
Imports System.Runtime.CompilerServices ' for extensions 

Module ArrayExtension ' custom method for array 
    Private accessKey As Object ' to store the generic lambda 

    Private Function ClassToPair(Of TValue, TKey)(obj As TValue) As KeyValuePair(Of TKey, TValue) ' the Converter 
     ' this is the one that avoids to mess around with delegates (as instances of delegates cannot be generic) 
     Dim a As PropertyAccessor(Of TValue, TKey) = DirectCast(accessKey, PropertyAccessor(Of TValue, TKey)) 
     Return New KeyValuePair(Of TKey, TValue)(a.accessProperty(obj), obj) 
    End Function 

    <Extension()> ' the type params list gets long, as we target it to be generic 
    Public Function toSortedPairedList(Of TValue, TKey, TSort, TReturn)(arr As TValue(), getKey As Func(Of TValue, TKey), 
      Optional getSortProperty As Func(Of KeyValuePair(Of TKey, TValue), TSort) = Nothing) _ 
      As List(Of KeyValuePair(Of TKey, TValue)) 

     If (getKey Is Nothing) OrElse (arr Is Nothing) OrElse (arr.Count < 1) Then Return New List(Of KeyValuePair(Of TKey, TValue)) ' empty list (instead of nothing) 
     Dim a As PropertyAccessor(Of TValue, TKey) = New PropertyAccessor(Of TValue, TKey)(getKey) 
     accessKey = a ' here we store/assign, so we can use it within the Converter function ClassToPair (with the delegate signature that introduced the problem) 

     ' Typecasting Generic parameter: https://stackoverflow.com/q/2891797/6215377 (can throw an exception; i.e. TSort = Integer, TKey = non-numeric String) 
     ' NOTE: this part is not essential (just an improvement) 
     ' NOTE II: we leave the Exception Catch to the caller (an improvement would be to throw an adapted Exception from here) 
     If getSortProperty Is Nothing Then getSortProperty = Function(x) CType(CObj(a.accessProperty(x.Value)), TSort) ' defaulting to sort by getKey(obj) 

     Dim auxList As List(Of KeyValuePair(Of TKey, TValue)) 
     auxList = Array.ConvertAll(arr, New Converter(Of TValue, KeyValuePair(Of TKey, TValue))(AddressOf ClassToPair(Of TValue, TKey))).ToList() 
     Return auxList.OrderBy(getSortProperty).ToList() ' .ToList to transform IEnumerable to the return type 
    End Function 

    ' Array Extension: - https://stackoverflow.com/a/30151099/4352306 
    ' irrelevant to this question (to Push into array) 
    <Extension()> 
    Public Sub Add(Of T)(ByRef arr As T(), item As T) 
     If arr IsNot Nothing Then 
      Array.Resize(arr, arr.Length + 1) 
      arr(arr.Length - 1) = item 
     Else 
      ReDim arr(0) 
      arr(0) = item 
     End If 
    End Sub 
End Module 

最后,这是测试,以显示其用法:

Public Class clsField 
    Public idx As String 
    Public name As String 
    Public weight As Long 

    Public Sub New(i As String, n As String, w As Long) 
     idx = i : name = n : weight = w 
    End Sub 

    Public Overrides Function ToString() As String 
     Return String.Format("{0}: {1} - {2}", name, idx, weight) 
    End Function 
End Class 

Public Class Container 
    Public fields() As clsField 

    ' here we call the extended method 
    Public Function getSortedPairs() As List(Of KeyValuePair(Of String, clsField)) 
     Return fields.toSortedPairedList(Of String, Long, List(Of KeyValuePair(Of String, clsField)))(Function(f) f.idx, Function(f) f.Value.weight) 
    End Function 

    ' it calls to the function above and converts back to List(Of clsField) 
    ' NOTE: not necessary; added to show more ideas of its usability 
    Public Function getSortedFields() As List(Of clsField) 
     Return getSortedPairs.ConvertAll(Function(pair) pair.Value) 
    End Function 
End Class 

Public Class Consumer 
    Public cont As Container 

    Public Sub New() 
     cont = New Container 
     cont.fields.Add(New clsField("ffq", "foo30004", 33)) 
     cont.fields.Add(New clsField("ffc", "foo9997", 55)) 
     cont.fields.Add(New clsField("ffp", "foo9908", 55)) 
     cont.fields.Add(New clsField("ffo", "foo100001", 22)) 
     cont.fields.Add(New clsField("ffx", "foo8885", 33)) 
     cont.fields.Add(New clsField("ffz", "foo70002", 22)) 
     cont.fields.Add(New clsField("ffy", "foo8806", 33)) 
     cont.fields.Add(New clsField("ffa", "foo9009", 55)) 
     cont.fields.Add(New clsField("ffb", "foo8000", 55)) 
     cont.fields.Add(New clsField("ffn", "foo7003", 22)) 
    End Sub 

    Public Sub printSortedFields() 
     For Each e As clsField In cont.getSortedFields() 
      Console.WriteLine(e.ToString()) 
      Debug.Print(e.ToString()) 
     Next 
    End Sub 

    Public Sub Main() 
     printSortedFields() 
    End Sub 
End Class 

此测试将这个输出 (创建条目以便最后一位数字确认测试):

foo100001: ffo - 22 
foo70002: ffz - 22 
foo7003: ffn - 22 
foo30004: ffq - 33 
foo8885: ffx - 33 
foo8806: ffy - 33 
foo9997: ffc - 55 
foo9908: ffp - 55 
foo9009: ffa - 55 
foo8000: ffb - 55 

希望这可以帮助有类似问题的人。虽然我不认为这是一个明确的解决方案,但它提供了一些方法来采取或丢弃方式来克服类型转换的困难时,使用generic methods and lambdas排序arrayslists

最佳希望

:所述排序方法被设计成它保留用于与相同的值的元素的原始插入顺序。