默认情况下对一组类进行排序 [英] Sort a group of Classes by default property

查看:102
本文介绍了默认情况下对一组类进行排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TL; DR:

是否可以将类集合/列表传递给排序算法,并使其返回已排序列表(最好是通过named/default类属性)?

/strong>

我最近一直在学习一些Python,并且对Sorted()函数印象深刻,该函数可以对任何迭代进行排序.对于数字来说,这很简单,但是对于类,可以像这样分配一个比较方法.该方法告诉比较运算符如何比较该类的2个实例.除其他功能外,它还允许您使用内置的排序算法对类的集合进行排序.

在VBA中,我在模仿这一点上取得了一半的成功.通过设置类的默认成员 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排序算法,则默认值*排序类应该没有问题.但是我更喜欢使用内置/库排序算法(出于相同的原因,任何人都将使用;清晰,高效,适当的错误处理等)

* 这些算法之一可以解决这个问题,尽管必须对其进行修改以切换整个类,而不是改变其值(通过添加Set s)

由于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?任何尝试使用更多内置排序算法的建议也将很有用!

解决方案

与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(即VBA中的Long),System.String和其他所有原始.NET类型,我认为 VBA基本类型确实可以通过.NET互操作正确地编组,但不能自定义类.

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

Implements IComparable

然后实现界面-应该看起来像这样(使用代码窗格下拉列表确保获得正确的签名-不要只复制粘贴此代码段:

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各项如何相互关联的接口,我看不出失败的原因.

TL;DR:

Is there any way to pass a class collection/list to a library sorting algorithm, and get it to return a sorted list (preferably by a named/default class property)?

I've recently been learning some Python, and was impressed by the Sorted() function, which can sort any iterable. For numbers this is straightforward, for classes however, it is possible to assign a comparison method like this. The method tells comparison operators how to compare 2 instances of the class. Amongst other things it allows you to use builtin sorting algorithms to sort a collection of the class.

In VBA I've been half successful in mimicking this. By setting a class' default member Attribute, you can use comparison operators (<,=,>=, etc.) on classes directly. Take the example class:

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

Two instances of this class can be compared:

 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

And if I was implementing a VBA sort algorithm that can sort values, there should be no problem sorting classes by default value*. However I would prefer to use a builtin/library sorting algorithm (for the same reasons anyone would; clarity, efficiency, proper error handling etc.)

*One of these algorithms would work for that, although must be modified to switch the entire class round, not the value of it (by adding Sets)

Since VBA comparison operators have no issue, I assumed the same would be true for whatever the library was using. However when I tried with an 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

I get an error

Failed to compare two elements in the array

So what's going on? Is it a flaw in my approach - is the default attribute not making it to the ArrayList? Or maybe the comparison operator in whatever language the library is written in is not as floopy-gloopy as the ones VBA and Python use? Any suggestions on more builtin sorting algorithms to try would be useful too!

解决方案

It's not about the VBA comparison operators, ArrayList is a .NET class, so you're in the .NET world when you use it.

arr.Add v 'no problem here

You're adding instances of the defaultProp class; it doesn't matter that you have a default property on the type, .NET doesn't care about default properties. If you want to sort DefaultValue values, then do arr.Add v.DefaultValue or arr.Add (v) - then your ArrayList will contain items of type Single, which it knows how to sort.

In order for ArrayList.Sort to work with instances of your custom class, its items need to implement the IComparable interface, which is the case for System.Int32 (i.e. Long in VBA), System.String and every other primitive .NET types, and I think the VBA primitive types would indeed marshal correctly through .NET interop - but not custom classes.

Try adding a reference to mscorlib.tlb, and then in the defaultProp class module, specify this (you can't implement an interface that's defined in a late-bound library):

Implements IComparable

Then implement the interface - should look something like this (use the codepane dropdowns to make sure to get the correct signature - don't just copy-paste this snippet:

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

Now that your custom class implements the interface ArrayList.Sort uses to determine how your defaultProp items relate to each other, I don't see a reason for it to fail.

这篇关于默认情况下对一组类进行排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆