默认情况下对一组类进行排序属性

时间:2017-12-12 15:19:53

标签: vba excel-vba class sorting icomparable

TL; DR:

有没有办法将类集合/列表传递给排序算法,并让它返回一个排序列表(最好是命名/默认类属性)? /强>

我最近学习了一些Python,Sorted()函数给我留下了深刻的印象,它可以对任何可迭代的函数进行排序。对于数字,这很简单,但对于类,可以分配比较方法like this。该方法告诉比较运算符如何比较该类的2个实例。除此之外,它还允许您使用内置排序算法对类的集合进行排序。

在VBA中,我已经成功地模仿了这一点。通过设置“default member Attribute”类,您可以直接在类上使用比较运算符(<=>=等)。以示例类为例:

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "defaultProp"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Private randVal As Single

Public Property Get DefaultValue() As Single
    Attribute Value.VB_UserMemId = 0
    DefaultValue = randVal
End Property

Private Property Let DefaultValue(ByVal value As Single)
    randVal = value
End Property

Private Sub Class_Initialize()
    DefaultValue = Rnd()
End Sub

可以比较这个类的两个实例:

 Dim instance1 As New defaultProp
 Dim instance2 As New defaultProp
 Debug.Print instance1.DefaultValue > instance2.DefaultValue
 Debug.Print instance1 > instance2 'exactly equivalent, as the DefaultValue has the correct Attribute

如果我正在实现可以对值进行排序的VBA排序算法,那么按默认值*排序类应该没有问题。但是我更喜欢使用内置/库排序算法(出于同样的原因,任何人都会这样做;清晰度,效率,正确的错误处理等)。

* these algorithms中的一个可以为此工作,但必须修改以循环整个类,而不是它的值(通过添加Set s) < / p>

由于VBA比较运算符没有问题,我认为对于库所使用的任何内容都是如此。但是当我尝试使用ArrayList

Sub testArrayList()
    Dim arr As Object
    Set arr = CreateObject("System.Collections.ArrayList")

    ' Initialise the ArrayList, for instance by generating random values
    Dim i As Long
    Dim v As defaultProp

    For i = 1 To 5
        Set v = New defaultProp
        arr.Add v 'no problem here
    Next i
    arr.Sort 'raises an error
End Sub

我收到错误

  

无法比较数组中的两个元素

那是怎么回事?这是我的方法中的一个缺陷 - 默认属性是不是ArrayList?或者也许用于编写库的任何语言的比较运算符都不像VBA和Python使用的那样具有floopy-gloopy?有关更多内置排序算法的任何建议都是有用的!

3 个答案:

答案 0 :(得分:4)

这与VBA比较运算符无关,ArrayList是一个.NET类,所以当你使用它时,你就处于.NET世界。

arr.Add v 'no problem here

您正在添加defaultProp类的实例;在类型上有一个默认属性并不重要,.NET不关心默认属性。如果您想对DefaultValue值进行排序,然后执行arr.Add v.DefaultValuearr.Add (v) - 那么您的ArrayList将包含Single类型的项目,它知道如何排序

为了使ArrayList.Sort能够使用自定义类的实例,其项目需要实现IComparable接口,System.Int32就是这种情况(即Long在VBA中),System.String和其他所有原始.NET类型,我认为 VBA原语类型确实会通过.NET互操作正确编组 - 而不是自定义类。

尝试添加对 mscorlib.tlb 的引用,然后在defaultProp类模块中指定此项(您无法实现在后期绑定库中定义的接口) :

Implements IComparable

然后实现界面 - 应该看起来像这样(使用codepane下拉菜单以确保获得正确的签名 - 不要只是复制粘贴此代码段

Private Function IComparable_CompareTo(ByVal obj As Variant) As Long
    Dim other As defaultProp
    Set other = obj
    ' return Less than zero (-1) if this object 
    ' is less than the object specified by the CompareTo method.

    ' return Zero (0) if this object is equal to the object 
    ' specified by the CompareTo method.

    ' return Greater than zero (1) if this object is greater than 
    ' the object specified by the CompareTo method.
End Function

现在您的自定义类实现了ArrayList.Sort用于确定defaultProp项与彼此之间关系的接口,我没有看到它失败的原因。

答案 1 :(得分:4)

IMO,你通过混合跨越边界的东西来滥用东西。您正在使用VBA的默认属性(我通常认为这是一种不好的做法),然后您使用.NET ArrayList并尝试Sort

我认为看看你是否可以在VBA类上实现IComparable然后让ArrayList使用IComparable接口将对象与其他对象进行比较会更合乎逻辑但是你想要它进行比较,而不使用任何黑客攻击的默认属性。

答案 2 :(得分:2)

如果将DefaultValue添加到google.com,它将起作用:

arr

显然Sub testArrayList() '... code For i = 1 To 5 Set v = New defaultProp arr.Add v.DefaultValue Next i arr.Sort End Sub .Sort的实现有点奇怪,不喜欢比较对象及其默认值(无法找到ArrayList方法的实现)。虽然,这将完美无瑕地运作:

Sort()

这是一种可能的排序实现,可以按预期用于For i = 1 To 5 Set v = New defaultProp arr.Add v Next i Debug.Print arr(1) > arr(2) 对象。但是,它不是arr库的一部分:

ArrayList

但排序还可以:

enter image description here