将Array转换为KeyValuePair的排序列表的通用函数(downvoted)

时间:2017-02-13 10:06:51

标签: arrays vb.net generics type-conversion extension-methods

我正在尝试从以下代码中获取generic method以扩展Array

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,它不接受新参数,前提是我可以传递一个函数来指定应该使用的key TInput

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

也许有一种方法Overload Array.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

1 个答案:

答案 0 :(得分:0)

我将回答我的问题。

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

它的优点是可以使用Converter,通过将Delegate存储到通用的模块变量中来解决KeySelector签名引入的限制:{{ 1}}。

我的猜测是,最好的方法是完整的泛型类的实现,它不仅仅依赖于Private accessKey As Object,而且还有更广泛的转换。

首先,为了将有助于KeyValuePairs KeySelector的{​​{1}}存储到 a ConvertAll中,泛型类是必要的,因此我们可以存储List(Of TValue)而没有编译错误(reference)(似乎没有更好的方法来解决List(Of KeyValuePair(Of TKey, TValue))引入的限制):< / p>

generic lambda

现在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对象中,{{1}在使用toSortedPairedList作为generic(将使用accessKey As Object的人)调用ConvertAll之前。这需要从ClassToPair到班级Converter的一些accessKey

type castings

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

Object

测试写入输出(创建的条目使姓名的最后一位确认测试):

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 进行排序时采取或放弃克服类型转换困难的方法。

祝福

注意排序方法的设计是为了保留具有相同值的元素的原始插入顺序。