我正在尝试从以下代码中获取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
并创建一个替代Delegate
到Converter
,其签名允许完成以下代码(显然不是为ConvertAll
和AddressOf 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 :(得分: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
进行排序时采取或放弃克服类型转换困难的方法。
祝福
注意:排序方法的设计是为了保留具有相同值的元素的原始插入顺序。