如何在Excel VBA中使用工具

时间:2020-12-12 17:33:31

I'm trying to implement some shapes for an engineering project and abstract it out for some common functions so that I can have a generalized program.

我正在为一个工程项目实现一些形状,并为一些常见的函数抽象出来,这样我就可以得到一个通用的程序。

What I'm trying to do is have an interface called cShape and have cRectangle and cCircle implement cShape

我要做的是有一个叫做cShape的接口,有c矩形和cCircle实现cShape

My code is below:

我的代码如下:

cShape interface

cShape接口

Option Explicit

Public Function getArea()
End Function

Public Function getInertiaX()
End Function

Public Function getInertiaY()
End Function

Public Function toString()
End Function

cRectangle class

cRectangle类

Option Explicit
Implements cShape

Public myLength As Double ''going to treat length as d
Public myWidth As Double ''going to treat width as b

Public Function getArea()
    getArea = myLength * myWidth
End Function

Public Function getInertiaX()
    getInertiaX = (myWidth) * (myLength ^ 3)
End Function

Public Function getInertiaY()
    getInertiaY = (myLength) * (myWidth ^ 3)
End Function

Public Function toString()
    toString = "This is a " & myWidth & " by " & myLength & " rectangle."
End Function

cCircle class

cCircle类

Option Explicit
Implements cShape

Public myRadius As Double

Public Function getDiameter()
    getDiameter = 2 * myRadius
End Function

Public Function getArea()
    getArea = Application.WorksheetFunction.Pi() * (myRadius ^ 2)
End Function

''Inertia around the X axis
Public Function getInertiaX()
    getInertiaX = Application.WorksheetFunction.Pi() / 4 * (myRadius ^ 4)
End Function

''Inertia around the Y axis
''Ix = Iy in a circle, technically should use same function
Public Function getInertiaY()
    getInertiaY = Application.WorksheetFunction.Pi() / 4 * (myRadius ^ 4)
End Function

Public Function toString()
    toString = "This is a radius " & myRadius & " circle."
End Function

The problem is that whenever I run my test cases, it comes up with the following error:

问题是,每当我运行测试用例时,都会出现以下错误:

Compile Error:

编译错误:

Object module needs to implement '~' for interface '~'

对象模块需要对接口“~”实现“~”

6 个解决方案

#1


73  

This is an esoteric OOP concept and there's a little more you need to do and understand to use a custom collection of shapes.

这是一个深奥的OOP概念,要使用自定义的形状集合,您还需要做一些工作并理解这些工作。

You may first want to go through this answer to get a general understanding of classes and interfaces in VBA.

您可能首先需要通过这个答案来获得VBA中的类和接口的一般理解。


Follow the below instructions

First open Notepad and copy-paste the below code

首先打开记事本,复制粘贴下面的代码

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1
END
Attribute VB_Name = "ShapesCollection"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

Dim myCustomCollection As Collection

Private Sub Class_Initialize()
    Set myCustomCollection = New Collection
End Sub

Public Sub Class_Terminate()
    Set myCustomCollection = Nothing
End Sub

Public Sub Add(ByVal Item As Object)
    myCustomCollection.Add Item
End Sub

Public Sub AddShapes(ParamArray arr() As Variant)
    Dim v As Variant
    For Each v In arr
        myCustomCollection.Add v
    Next
End Sub

Public Sub Remove(index As Variant)
    myCustomCollection.Remove (index)
End Sub

Public Property Get Item(index As Long) As cShape
    Set Item = myCustomCollection.Item(index)
End Property

Public Property Get Count() As Long
    Count = myCustomCollection.Count
End Property

Public Property Get NewEnum() As IUnknown
    Attribute NewEnum.VB_UserMemId = -4
    Attribute NewEnum.VB_MemberFlags = "40"
    Set NewEnum = myCustomCollection.[_NewEnum]
End Property

Save the file as ShapesCollection.cls to your desktop.

将文件保存为ShapesCollection。cls到您的桌面。

Make sure you are saving it with the *.cls extension and not ShapesCollection.cls.txt

一定要用*保存。cls扩展,而不是ShapesCollection.cls.txt

Now open you Excel file, go to VBE ALT+F11 and right click in the Project Explorer. Select Import File from the drop-down menu and navigate to the file.

现在打开Excel文件,转到VBE ALT+F11,在Project Explorer中右键单击。从下拉菜单中选择Import File并导航到该文件。

如何在Excel VBA中使用工具

NB: You needed to save the code in a .cls file first and then import it because VBEditor does not allow you to use Attributes. The attributes allow you to specify the default member in the iteration and use the for each loop on custom collection classes

NB:您需要先将代码保存在.cls文件中,然后导入它,因为VBEditor不允许您使用属性。属性允许您在迭代中指定默认成员,并在自定义集合类上使用for each循环。

See more:

看到更多:

Now Insert 3 class modules. Rename accordingly and copy-paste the code

现在插入3个类模块。相应地重命名并复制代码。

cShape this is your Interface

这是你的界面

Public Function GetArea() As Double
End Function

Public Function GetInertiaX() As Double
End Function

Public Function GetInertiaY() As Double
End Function

Public Function ToString() As String
End Function

cCircle

cCircle

Option Explicit

Implements cShape

Public Radius As Double

Public Function GetDiameter() As Double
    GetDiameter = 2 * Radius
End Function

Public Function GetArea() As Double
    GetArea = Application.WorksheetFunction.Pi() * (Radius ^ 2)
End Function

''Inertia around the X axis
Public Function GetInertiaX() As Double
    GetInertiaX = Application.WorksheetFunction.Pi() / 4 * (Radius ^ 4)
End Function

''Inertia around the Y axis
''Ix = Iy in a circle, technically should use same function
Public Function GetInertiaY() As Double
    GetInertiaY = Application.WorksheetFunction.Pi() / 4 * (Radius ^ 4)
End Function

Public Function ToString() As String
    ToString = "This is a radius " & Radius & " circle."
End Function

'interface functions
Private Function cShape_getArea() As Double
    cShape_getArea = GetArea
End Function

Private Function cShape_getInertiaX() As Double
    cShape_getInertiaX = GetInertiaX
End Function

Private Function cShape_getInertiaY() As Double
    cShape_getInertiaY = GetInertiaY
End Function

Private Function cShape_toString() As String
    cShape_toString = ToString
End Function

cRectangle

cRectangle

Option Explicit

Implements cShape

Public Length As Double ''going to treat length as d
Public Width As Double ''going to treat width as b

Public Function GetArea() As Double
    GetArea = Length * Width
End Function

Public Function GetInertiaX() As Double
    GetInertiaX = (Width) * (Length ^ 3)
End Function

Public Function GetInertiaY() As Double
    GetInertiaY = (Length) * (Width ^ 3)
End Function

Public Function ToString() As String
    ToString = "This is a " & Width & " by " & Length & " rectangle."
End Function

' interface properties
Private Function cShape_getArea() As Double
    cShape_getArea = GetArea
End Function

Private Function cShape_getInertiaX() As Double
    cShape_getInertiaX = GetInertiaX
End Function

Private Function cShape_getInertiaY() As Double
    cShape_getInertiaY = GetInertiaY
End Function

Private Function cShape_toString() As String
    cShape_toString = ToString
End Function

You need to Insert a standard Module now and copy-paste the below code

现在需要插入一个标准模块并复制粘贴下面的代码

Module1

Module1的

Option Explicit

Sub Main()

    Dim shapes As ShapesCollection
    Set shapes = New ShapesCollection

    AddShapesTo shapes

    Dim iShape As cShape
    For Each iShape In shapes
        'If TypeOf iShape Is cCircle Then
            Debug.Print iShape.ToString, "Area: " & iShape.GetArea, "InertiaX: " & iShape.GetInertiaX, "InertiaY:" & iShape.GetInertiaY
        'End If
    Next

End Sub


Private Sub AddShapesTo(ByRef shapes As ShapesCollection)

    Dim c1 As New cCircle
    c1.Radius = 10.5

    Dim c2 As New cCircle
    c2.Radius = 78.265

    Dim r1 As New cRectangle
    r1.Length = 80.87
    r1.Width = 20.6

    Dim r2 As New cRectangle
    r2.Length = 12.14
    r2.Width = 40.74

    shapes.AddShapes c1, c2, r1, r2
End Sub

Run the Main Sub and check out the results in the Immediate Window CTRL+G

运行主子文件并在当前窗口中检查结果并按CTRL+G

如何在Excel VBA中使用工具


Comments and explanation:

In your ShapesCollection class module there are 2 subs for adding items to the collection.

在ShapesCollection类模块中,有两个子项可以添加到集合中。

The first method Public Sub Add(ByVal Item As Object) simply takes a class instance and adds it to the collection. You can use it in your Module1 like this

第一个方法Public Sub Add(ByVal Item As Object)只是接受一个类实例并将其添加到集合中。你可以像这样在你的Module1中使用它。

Dim c1 As New cCircle
shapes.Add c1

The Public Sub AddShapes(ParamArray arr() As Variant) allows you to add multiple objects at the same time separating them by a , comma in the same exact way as the AddShapes() Sub does.

公共子AddShapes(ParamArray arr() As Variant))允许您同时添加多个对象,并用逗号分隔它们,其方式与AddShapes()子对象相同。

It's quite a better design than adding each object separately, but it's up to you which one you are going to go for.

这比单独添加每个对象要好得多,但这取决于您选择哪个对象。

Notice how I have commented out some code in the loop

注意我如何在循环中注释了一些代码

Dim iShape As cShape
For Each iShape In shapes
    'If TypeOf iShape Is cCircle Then
        Debug.Print iShape.ToString, "Area: " & iShape.GetArea, "InertiaX: " & iShape.GetInertiaX, "InertiaY:" & iShape.GetInertiaY
    'End If
Next

If you remove comments from the 'If and 'End If lines you will be able to print only the cCircle objects. This would be really useful if you could use delegates in VBA but you can't so I have shown you the other way to print only one type of objects. You can obviously modify the If statement to suit your needs or simply print out all objects. Again, it is up to you how you are going to handle your data :)

如果您删除了“If”和“If”结尾的注释,您将能够只打印cCircle对象。如果您可以在VBA中使用委托,这将非常有用,但是您不能这样做,因此我向您展示了另一种方法,即只打印一种类型的对象。显然可以修改If语句以满足您的需要,或者简单地打印出所有对象。同样,如何处理您的数据取决于您自己:)

#2


12  

Here are some theoretical and practical contributions to the answers given, in case people arrive here who wonder what implements / interfaces are about.

这里有一些理论和实践上的贡献,如果有人来到这里想知道实现/接口是什么。

As we know, VBA doesn't support inheritance, hence we might almost blindly use interfaces to implement common properties/behaviour across different classes.
Still, I think that it is useful to describe what the conceptual difference is between the two to see why it matters later on.

正如我们所知道的,VBA不支持继承,因此我们可能几乎盲目地使用接口来实现跨不同类的公共属性/行为。尽管如此,我认为描述这两者之间的概念差异是有用的,以了解它为什么在以后重要。

  • Inheritance: defines an is-a relationship (a square is-a shape);
  • 继承:定义一个is-a关系(一个方形的is-a形状);
  • Interfaces: define a must-do relationship (a typical example is the drawable interface that prescribes that drawable object must implement the method draw). This means that classes originating from different root classes can implement common behaviour.
  • 接口:定义一个必须要做的关系(典型的例子是可绘制接口,它规定可绘制对象必须实现方法绘制)。这意味着来自不同根类的类可以实现公共行为。

Inheritance means that a baseclass (some physical or conceptual archetype) is extended, whereas interfaces implement a set of properties/methods that define a certain behaviour.
As such, one would say that Shape is a base class from which all other shapes inherit, one that may implement the drawable interface to make all shapes drawable. This interface would be a contract that guarantees that every Shape has a draw method, specifying how/where a shape should be drawn: a circle may - or may not - be drawn differently from a square.

继承意味着对baseclass(一些物理或概念原型)进行了扩展,而接口实现了一组属性/方法来定义某种行为。因此,可以说形状是所有其他形状继承的基类,它可以实现drawable接口,使所有形状都可以绘制。这个界面将是一个合同,保证每个形状都有一个绘制方法,指定一个形状应该如何/在哪里绘制:圆可以——也可以不可以——与正方形绘制不同。

class IDrawable:

类IDrawable:

'IDrawable interface, defining what methods drawable objects have access to
Public Function draw()
End Function

Since VBA doesn't support inheritance, we are automatically forced to opt for creating an interface IShape that guarantees certain properties/behaviour to be implemented by the generic shapes (square, circle, etc), rather than creating an abstract Shape baseclass from which we can extend.

由于VBA不支持继承,我们*选择创建一个接口IShape,该接口保证某些属性/行为由泛型(正方形、圆形等)实现,而不是创建一个抽象的形状基类,我们可以从中扩展。

class IShape:

类IShape:

'Get the area of a shape
Public Function getArea() As Double
End Function

The part where we get in trouble is when we want to make every Shape drawable.
Unfortunately, since IShape is an interface and not a base class in VBA, we cannot implement the drawable interface in the base class. It appears that VBA does not allow us to have one interface implement another; after having tested this, the compiler doesn't seem to provide the desired behaviour. In other words, we cannot implement IDrawable within IShape, and expect instances of IShape to be forced to implement IDrawable methods because of this.
We are forced to implement this interface to every generic shape class that implements the IShape interface, and luckily VBA allows multiple interfaces to be implemented.

我们遇到麻烦的部分是当我们想让每个形状都可绘制时。不幸的是,由于IShape是一个接口,而不是VBA中的基类,所以我们无法在基类中实现drawable接口。看来VBA不允许我们有一个接口实现另一个接口;经过测试之后,编译器似乎没有提供所需的行为。换句话说,我们不能在IShape中实现IDrawable,因此我们期望IShape的实例*实现IDrawable方法。我们*将这个接口实现到每个实现IShape接口的泛型类,幸运的是VBA允许实现多个接口。

class cSquare:

类cSquare:

Option Explicit

Implements iShape
Implements IDrawable

Private pWidth          As Double
Private pHeight         As Double
Private pPositionX      As Double
Private pPositionY      As Double

Public Function iShape_getArea() As Double
    getArea = pWidth * pHeight
End Function

Public Function IDrawable_draw()
    debug.print "Draw square method"
End Function

'Getters and setters

The part that follows now is where the typical use / benefits of an interface come into play.

下面的部分是接口的典型使用/好处发挥作用的部分。

Let's start off our code by writing a factory that returns a new square. (This is just a workaround for our inability to send arguments directly to the constructor):

让我们从编写一个返回新正方形的工厂开始。(这只是我们无法直接向构造函数发送参数的一个解决方案):

module mFactory:

模块mFactory:

Public Function createSquare(width, height, x, y) As cSquare

    Dim square As New cSquare

    square.width = width
    square.height = height
    square.positionX = x
    square.positionY = y

    Set createSquare = square

End Function

Our main code will use the factory to create a new Square:

我们的主代码将使用工厂创建一个新的广场:

Dim square          As cSquare

Set square = mFactory.createSquare(5, 5, 0, 0)

When you look at the methods that you have at your disposal, you'll notice that you logically get access to all the methods that are defined on the cSquare class:

当您查看您可以使用的方法时,您将注意到您在逻辑上可以访问cSquare类中定义的所有方法:

如何在Excel VBA中使用工具

We'll see later on why this is relevant.

我们将在后面看到为什么这是相关的。

Now you should wonder what will happen if you really want to create a collection of drawable objects. Your app could happen to contain objects that aren't shapes, but that are yet drawable. Theoretically, nothing prevents you from having an IComputer interface that can be drawn (may be some clipart or whatever).
The reason why you might want to have a collection of drawable objects, is because you may want to render them in a loop at a certain point in the app lifecycle.

现在,如果您真的想创建一个可绘制对象集合,您应该想知道会发生什么。你的应用程序可能碰巧包含了不是形状的对象,但仍然是可绘制的。从理论上讲,没有什么可以阻止您拥有可以绘制的IComputer接口(可能是某个clipart或其他什么)。您可能希望拥有可绘制对象集合的原因是,您可能希望在应用程序生命周期的某个点将它们呈现在循环中。

In this case I will write a decorator class that wraps a collection (we'll see why). class collDrawables:

在本例中,我将编写包装集合的decorator类(我们将看到原因)。类collDrawables:

Option Explicit

Private pSize As Integer
Private pDrawables As Collection

'constructor
Public Sub class_initialize()
    Set pDrawables = New Collection
End Sub

'Adds a drawable to the collection
Public Sub add(cDrawable As IDrawable)
    pDrawables.add cDrawable

    'Increase collection size
    pSize = pSize + 1

End Sub

The decorator allows you to add some convenience methods that native vba collections don't provide, but the actual point here is that the collection will only accept objects that are drawable (implement the IDrawable interface). If we would try to add an object that is not drawable, a type mismatch would be thrown (only drawable objects allowed!).

decorator允许您添加一些本地vba集合不提供的便利方法,但这里的实际点是集合只接受可绘制的对象(实现IDrawable接口)。如果我们尝试添加一个不可绘制的对象,则会抛出一个类型不匹配(只允许绘制对象!)

So we might want to loop over a collection of drawable objects to render them. Allowing a non-drawable object into the collection would result in a bug. A render loop could look like this:

因此,我们可能希望对可绘制对象的集合进行循环以呈现它们。允许一个不可拖动的对象进入集合将导致错误。渲染循环如下:

Option Explicit

选项显式

Public Sub app()

    Dim obj             As IDrawable
    Dim square_1        As IDrawable
    Dim square_2        As IDrawable
    Dim computer        As IDrawable
    Dim person          as cPerson 'Not drawable(!) 
    Dim collRender      As New collDrawables

    Set square_1 = mFactory.createSquare(5, 5, 0, 0)
    Set square_2 = mFactory.createSquare(10, 5, 0, 0)
    Set computer = mFactory.createComputer(20, 20)

    collRender.add square_1
    collRender.add square_2
    collRender.add computer

    'This is the loop, we are sure that all objects are drawable! 
    For Each obj In collRender.getDrawables
        obj.draw
    Next obj

End Sub

Note that the above code adds a lot of transparency: we declared the objects as IDrawable, which makes it transparent that the loop will never fail, since the draw method is available on all objects within the collection.
If we would try to add a Person to the collection, it would throw a type mismatch if this Person class did not implement the drawable interface.

注意,上面的代码增加了很多透明度:我们声明对象为IDrawable,这使得循环不会失败变得透明,因为draw方法在集合中的所有对象上都是可用的。如果我们尝试将Person添加到集合中,如果这个Person类没有实现drawable接口,则会抛出类型不匹配。

But perhaps the most relevant reason why declaring an object as an interface is important, is because we only want to expose the methods that were defined in the interface, and not those public methods that were defined on the individual classes as we've seen before.

但是,将对象声明为接口的最相关的原因可能是,我们只想公开在接口中定义的方法,而不是像我们以前看到的那样在单个类上定义的那些公共方法。

Dim square_1        As IDrawable 

如何在Excel VBA中使用工具

Not only are we certain that square_1 has a draw method, but it also ensure that only methods defined by IDrawable get exposed.
For a square, the benefit of this might not be immediately clear, but let's have a look at an analogy from the Java collections framework that is much clearer.

我们不仅可以确定square_1有一个绘制方法,而且它还确保只有通过IDrawable定义的方法才能被公开。对于square来说,这样做的好处可能不会马上显现出来,但让我们看一下Java collections框架的一个类比,这个类比要清晰得多。

Imagine that you have a generic interface called IList that defines a set of methods applicable on different types of lists. Each type of list is a specific class that implements the IList interface, defining their own behaviour, and possibly adding more methods of their own on top.

假设您有一个名为IList的通用接口,它定义了一组适用于不同类型列表的方法。每种类型的列表都是一个特定的类,它实现IList接口,定义它们自己的行为,并可能在上面添加更多自己的方法。

We declare the list as follows:

我们声明如下:

dim myList as IList 'Declare as the interface! 

set myList = new ArrayList 'Implements the interface of IList only, ArrayList allows random (index-based) access 

In the above code, declaring the list as IList ensures that you won't use ArrayList-specific methods, but only methods that are prescribed by the interface. Imagine that you declared the list as follows:

在上面的代码中,将列表声明为IList可以确保您不会使用特定于arraylist的方法,而只使用接口指定的方法。假设您声明如下列表:

dim myList as ArrayList 'We don't want this

You will have access to the public methods that are specifically defined on the ArrayList class. Sometimes this might be desired, but often we just want to take benefit of the internal class behaviour, and not defined by the class specific public methods.
The benefit becomes clear if we use this ArrayList 50 more times in our code, and suddenly we find out that we're better off using a LinkedList (which allows specific internal behaviour related to this type of List).

您将可以访问ArrayList类中特定定义的公共方法。有时这可能是需要的,但通常我们只是想从内部的类行为中获益,而不是由类特定的公共方法定义。如果我们在代码中使用了这个ArrayList 50多次,那么我们就会发现,使用LinkedList(允许与此类列表相关的特定内部行为)会更好。

If we complied to the interface, we can change the line:

如果我们遵从界面,我们可以改变线条:

set myList = new ArrayList

to:

:

set myList = new LinkedList 

and none of the other code will break as the interface makes sure that the contract is fulfilled, ie. only public methods defined on IList are used, so the different types of lists are swappable over time.

当接口确保合同得以履行时,其他任何代码都不会中断。只使用在IList上定义的公共方法,所以不同类型的列表可以随时间变化。

A final thing (perhaps lesser known behaviour in VBA) is that you can give an interface a default implementation

最后一件事(VBA中较不为人知的行为)是,您可以为接口提供一个默认实现

We can define an interface in the following way:

我们可以这样定义一个接口:

IDrawable:

IDrawable:

Public Function draw()
    Debug.Print "Draw interface method"
End Function

and a class that implements the draw method as well:

以及实现绘制方法的类:

cSquare:

cSquare:

implements IDrawable 
Public Function draw()
    Debug.Print "Draw square method" 
End Function

We can switch between the implementations the following way:

我们可以通过以下方式在实现之间进行切换:

Dim square_1        As IDrawable

Set square_1 = New IDrawable
square_1.draw 'Draw interface method
Set square_1 = New cSquare
square_1.draw 'Draw square method    

This is not possible if you declare the variable as cSquare.
I can't immediately think of a good example when this might be useful, but it is technically possible if you test it.

如果将变量声明为cSquare,这是不可能的。我不能马上想到一个很好的例子,当这个可能有用的时候,但是如果您测试它,从技术上来说是可能的。

#3


10  

There are two undocumented additions about VBA and "Implements" statement.

关于VBA和“实现”语句,有两个没有文档说明的添加。

  1. VBA does not support undescore character '_' in a method name of an inherited interface of a derived class. F.e. it will not compile a code with method such as cShape.get_area (tested under Excel 2007): VBA will output the compile error above for any derived class.

    VBA不支持派生类继承接口的方法名中undescore字符“_”。它不会用cShape这样的方法编译代码。get_area(在excel2007下测试):VBA将为任何派生类输出上面的编译错误。

  2. If a derived class does not implement the own method named as in the interface, VBA compiles a code successfully, but the method will be inacessiable through a variable of the derived class type.

    如果派生类没有实现接口中命名的方法,VBA将成功编译代码,但是该方法将通过派生类类型的变量不可访问。

#4


8  

We must implement all methods of interface in the class which it is used.

我们必须实现它所使用的类中的所有接口方法。

cCircle Class

cCircle类

Option Explicit
Implements cShape

Public myRadius As Double

Public Function getDiameter()
    getDiameter = 2 * myRadius
End Function

Public Function getArea()
    getArea = Application.WorksheetFunction.Pi() * (myRadius ^ 2)
End Function

''Inertia around the X axis
Public Function getInertiaX()
    getInertiaX = Application.WorksheetFunction.Pi() / 4 * (myRadius ^ 4)
End Function

''Inertia around the Y axis
''Ix = Iy in a circle, technically should use same function
Public Function getIntertiaY()
    getIntertiaY = Application.WorksheetFunction.Pi() / 4 * (myRadius ^ 4)
End Function

Public Function toString()
    toString = "This is a radius " & myRadius & " circle."
End Function

Private Function cShape_getArea() As Variant

End Function

Private Function cShape_getInertiaX() As Variant

End Function

Private Function cShape_getIntertiaY() As Variant

End Function

Private Function cShape_toString() As Variant

End Function

cRectangle Class

cRectangle类

Option Explicit
Implements cShape

Public myLength As Double ''going to treat length as d
Public myWidth As Double ''going to treat width as b
Private getIntertiaX As Double

Public Function getArea()
    getArea = myLength * myWidth
End Function

Public Function getInertiaX()
    getIntertiaX = (myWidth) * (myLength ^ 3)
End Function

Public Function getIntertiaY()
    getIntertiaY = (myLength) * (myWidth ^ 3)
End Function

Public Function toString()
    toString = "This is a " & myWidth & " by " & myLength & " rectangle."
End Function

Private Function cShape_getArea() As Variant

End Function

Private Function cShape_getInertiaX() As Variant

End Function

Private Function cShape_getIntertiaY() As Variant

End Function

Private Function cShape_toString() As Variant

End Function

cShape Class

cShape类

Option Explicit

Public Function getArea()
End Function

Public Function getInertiaX()
End Function

Public Function getIntertiaY()
End Function

Public Function toString()
End Function

如何在Excel VBA中使用工具

#5


0  

Very interesting post to understand simply why and when an interface can be useful! But I think your final example about the default implementation is incorrect. The first call to the draw method of square_1 instantiated as IDrawable correctly prints the result you give, but the second call to the draw method of square_1 instantiated as cSquare is incorrect, nothing is printed. 3 different methods actually come into play:

非常有趣的文章,简单地理解为什么和什么时候界面可以有用!但是我认为关于默认实现的最后一个示例是不正确的。将square_1实例化为IDrawable的绘制方法的第一个调用正确地打印了您所给出的结果,但是将square_1的绘制方法实例化为cSquare的第二个调用是错误的,没有打印任何内容。实际上有三种不同的方法在起作用:

IDrawable.cls:

IDrawable.cls:

Public Function draw()
    Debug.Print "Interface Draw method"
End Function

cSquare.cls:

cSquare.cls:

Implements IDrawable

Public Function draw()
    Debug.Print "Class Draw method"
End Function

Public Function IDrawable_draw()
    Debug.Print "Interfaced Draw method"
End Function

Standard module:

标准模块:

Sub Main()
    Dim square_1 As Class6_Methods_IDrawable
    Set square_1 = New Class6_Methods_IDrawable
    Debug.Print "square_1 : ";
    square_1.draw

    Dim square_2 As Class6_Methods_cSquare
    Set square_2 = New Class6_Methods_cSquare
    Debug.Print "square_2 : ";
    square_2.draw 

    Dim square_3 As Class6_Methods_IDrawable
    Set square_3 = New Class6_Methods_cSquare
    Debug.Print "square_3 : ";
    square_3.draw
End Sub

Results in:

结果:

square_1 : Interface Draw method
square_2 : Class Draw method
square_3 : Interfaced Draw method

#6


0  

Quick Fix of Syntax

If the interface ISomeInterface has:

如果接口ISomeInterface有:

Public Sub someMethod()
    ' Interface, no code
End Sub

Then the implementation needs to be like:

然后实现需要如下:

Implements ISomeInterface

Public Sub ISomeInterface_someMethod()
    '      ^^^^^^^^^^^^^^^  ' If missing: Compile Error 
    ' Code goes here
End Sub

A nice approach:

一个很好的方法:

Implements ISomeInterface

Private Sub someMethod()
    ' Business logic goes here
End Sub

Public Sub ISomeInterface_someMethod()
    someMethod ' i.e. Business logic in 1 place: someMethod
End Sub

That said, the other answers are very much worth reading.

也就是说,其他的答案非常值得一读。

#1


73  

This is an esoteric OOP concept and there's a little more you need to do and understand to use a custom collection of shapes.

这是一个深奥的OOP概念,要使用自定义的形状集合,您还需要做一些工作并理解这些工作。

You may first want to go through this answer to get a general understanding of classes and interfaces in VBA.

您可能首先需要通过这个答案来获得VBA中的类和接口的一般理解。


Follow the below instructions

First open Notepad and copy-paste the below code

首先打开记事本,复制粘贴下面的代码

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1
END
Attribute VB_Name = "ShapesCollection"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

Dim myCustomCollection As Collection

Private Sub Class_Initialize()
    Set myCustomCollection = New Collection
End Sub

Public Sub Class_Terminate()
    Set myCustomCollection = Nothing
End Sub

Public Sub Add(ByVal Item As Object)
    myCustomCollection.Add Item
End Sub

Public Sub AddShapes(ParamArray arr() As Variant)
    Dim v As Variant
    For Each v In arr
        myCustomCollection.Add v
    Next
End Sub

Public Sub Remove(index As Variant)
    myCustomCollection.Remove (index)
End Sub

Public Property Get Item(index As Long) As cShape
    Set Item = myCustomCollection.Item(index)
End Property

Public Property Get Count() As Long
    Count = myCustomCollection.Count
End Property

Public Property Get NewEnum() As IUnknown
    Attribute NewEnum.VB_UserMemId = -4
    Attribute NewEnum.VB_MemberFlags = "40"
    Set NewEnum = myCustomCollection.[_NewEnum]
End Property

Save the file as ShapesCollection.cls to your desktop.

将文件保存为ShapesCollection。cls到您的桌面。

Make sure you are saving it with the *.cls extension and not ShapesCollection.cls.txt

一定要用*保存。cls扩展,而不是ShapesCollection.cls.txt

Now open you Excel file, go to VBE ALT+F11 and right click in the Project Explorer. Select Import File from the drop-down menu and navigate to the file.

现在打开Excel文件,转到VBE ALT+F11,在Project Explorer中右键单击。从下拉菜单中选择Import File并导航到该文件。

如何在Excel VBA中使用工具

NB: You needed to save the code in a .cls file first and then import it because VBEditor does not allow you to use Attributes. The attributes allow you to specify the default member in the iteration and use the for each loop on custom collection classes

NB:您需要先将代码保存在.cls文件中,然后导入它,因为VBEditor不允许您使用属性。属性允许您在迭代中指定默认成员,并在自定义集合类上使用for each循环。

See more:

看到更多:

Now Insert 3 class modules. Rename accordingly and copy-paste the code

现在插入3个类模块。相应地重命名并复制代码。

cShape this is your Interface

这是你的界面

Public Function GetArea() As Double
End Function

Public Function GetInertiaX() As Double
End Function

Public Function GetInertiaY() As Double
End Function

Public Function ToString() As String
End Function

cCircle

cCircle

Option Explicit

Implements cShape

Public Radius As Double

Public Function GetDiameter() As Double
    GetDiameter = 2 * Radius
End Function

Public Function GetArea() As Double
    GetArea = Application.WorksheetFunction.Pi() * (Radius ^ 2)
End Function

''Inertia around the X axis
Public Function GetInertiaX() As Double
    GetInertiaX = Application.WorksheetFunction.Pi() / 4 * (Radius ^ 4)
End Function

''Inertia around the Y axis
''Ix = Iy in a circle, technically should use same function
Public Function GetInertiaY() As Double
    GetInertiaY = Application.WorksheetFunction.Pi() / 4 * (Radius ^ 4)
End Function

Public Function ToString() As String
    ToString = "This is a radius " & Radius & " circle."
End Function

'interface functions
Private Function cShape_getArea() As Double
    cShape_getArea = GetArea
End Function

Private Function cShape_getInertiaX() As Double
    cShape_getInertiaX = GetInertiaX
End Function

Private Function cShape_getInertiaY() As Double
    cShape_getInertiaY = GetInertiaY
End Function

Private Function cShape_toString() As String
    cShape_toString = ToString
End Function

cRectangle

cRectangle

Option Explicit

Implements cShape

Public Length As Double ''going to treat length as d
Public Width As Double ''going to treat width as b

Public Function GetArea() As Double
    GetArea = Length * Width
End Function

Public Function GetInertiaX() As Double
    GetInertiaX = (Width) * (Length ^ 3)
End Function

Public Function GetInertiaY() As Double
    GetInertiaY = (Length) * (Width ^ 3)
End Function

Public Function ToString() As String
    ToString = "This is a " & Width & " by " & Length & " rectangle."
End Function

' interface properties
Private Function cShape_getArea() As Double
    cShape_getArea = GetArea
End Function

Private Function cShape_getInertiaX() As Double
    cShape_getInertiaX = GetInertiaX
End Function

Private Function cShape_getInertiaY() As Double
    cShape_getInertiaY = GetInertiaY
End Function

Private Function cShape_toString() As String
    cShape_toString = ToString
End Function

You need to Insert a standard Module now and copy-paste the below code

现在需要插入一个标准模块并复制粘贴下面的代码

Module1

Module1的

Option Explicit

Sub Main()

    Dim shapes As ShapesCollection
    Set shapes = New ShapesCollection

    AddShapesTo shapes

    Dim iShape As cShape
    For Each iShape In shapes
        'If TypeOf iShape Is cCircle Then
            Debug.Print iShape.ToString, "Area: " & iShape.GetArea, "InertiaX: " & iShape.GetInertiaX, "InertiaY:" & iShape.GetInertiaY
        'End If
    Next

End Sub


Private Sub AddShapesTo(ByRef shapes As ShapesCollection)

    Dim c1 As New cCircle
    c1.Radius = 10.5

    Dim c2 As New cCircle
    c2.Radius = 78.265

    Dim r1 As New cRectangle
    r1.Length = 80.87
    r1.Width = 20.6

    Dim r2 As New cRectangle
    r2.Length = 12.14
    r2.Width = 40.74

    shapes.AddShapes c1, c2, r1, r2
End Sub

Run the Main Sub and check out the results in the Immediate Window CTRL+G

运行主子文件并在当前窗口中检查结果并按CTRL+G

如何在Excel VBA中使用工具


Comments and explanation:

In your ShapesCollection class module there are 2 subs for adding items to the collection.

在ShapesCollection类模块中,有两个子项可以添加到集合中。

The first method Public Sub Add(ByVal Item As Object) simply takes a class instance and adds it to the collection. You can use it in your Module1 like this

第一个方法Public Sub Add(ByVal Item As Object)只是接受一个类实例并将其添加到集合中。你可以像这样在你的Module1中使用它。

Dim c1 As New cCircle
shapes.Add c1

The Public Sub AddShapes(ParamArray arr() As Variant) allows you to add multiple objects at the same time separating them by a , comma in the same exact way as the AddShapes() Sub does.

公共子AddShapes(ParamArray arr() As Variant))允许您同时添加多个对象,并用逗号分隔它们,其方式与AddShapes()子对象相同。

It's quite a better design than adding each object separately, but it's up to you which one you are going to go for.

这比单独添加每个对象要好得多,但这取决于您选择哪个对象。

Notice how I have commented out some code in the loop

注意我如何在循环中注释了一些代码

Dim iShape As cShape
For Each iShape In shapes
    'If TypeOf iShape Is cCircle Then
        Debug.Print iShape.ToString, "Area: " & iShape.GetArea, "InertiaX: " & iShape.GetInertiaX, "InertiaY:" & iShape.GetInertiaY
    'End If
Next

If you remove comments from the 'If and 'End If lines you will be able to print only the cCircle objects. This would be really useful if you could use delegates in VBA but you can't so I have shown you the other way to print only one type of objects. You can obviously modify the If statement to suit your needs or simply print out all objects. Again, it is up to you how you are going to handle your data :)

如果您删除了“If”和“If”结尾的注释,您将能够只打印cCircle对象。如果您可以在VBA中使用委托,这将非常有用,但是您不能这样做,因此我向您展示了另一种方法,即只打印一种类型的对象。显然可以修改If语句以满足您的需要,或者简单地打印出所有对象。同样,如何处理您的数据取决于您自己:)

#2


12  

Here are some theoretical and practical contributions to the answers given, in case people arrive here who wonder what implements / interfaces are about.

这里有一些理论和实践上的贡献,如果有人来到这里想知道实现/接口是什么。

As we know, VBA doesn't support inheritance, hence we might almost blindly use interfaces to implement common properties/behaviour across different classes.
Still, I think that it is useful to describe what the conceptual difference is between the two to see why it matters later on.

正如我们所知道的,VBA不支持继承,因此我们可能几乎盲目地使用接口来实现跨不同类的公共属性/行为。尽管如此,我认为描述这两者之间的概念差异是有用的,以了解它为什么在以后重要。

  • Inheritance: defines an is-a relationship (a square is-a shape);
  • 继承:定义一个is-a关系(一个方形的is-a形状);
  • Interfaces: define a must-do relationship (a typical example is the drawable interface that prescribes that drawable object must implement the method draw). This means that classes originating from different root classes can implement common behaviour.
  • 接口:定义一个必须要做的关系(典型的例子是可绘制接口,它规定可绘制对象必须实现方法绘制)。这意味着来自不同根类的类可以实现公共行为。

Inheritance means that a baseclass (some physical or conceptual archetype) is extended, whereas interfaces implement a set of properties/methods that define a certain behaviour.
As such, one would say that Shape is a base class from which all other shapes inherit, one that may implement the drawable interface to make all shapes drawable. This interface would be a contract that guarantees that every Shape has a draw method, specifying how/where a shape should be drawn: a circle may - or may not - be drawn differently from a square.

继承意味着对baseclass(一些物理或概念原型)进行了扩展,而接口实现了一组属性/方法来定义某种行为。因此,可以说形状是所有其他形状继承的基类,它可以实现drawable接口,使所有形状都可以绘制。这个界面将是一个合同,保证每个形状都有一个绘制方法,指定一个形状应该如何/在哪里绘制:圆可以——也可以不可以——与正方形绘制不同。

class IDrawable:

类IDrawable:

'IDrawable interface, defining what methods drawable objects have access to
Public Function draw()
End Function

Since VBA doesn't support inheritance, we are automatically forced to opt for creating an interface IShape that guarantees certain properties/behaviour to be implemented by the generic shapes (square, circle, etc), rather than creating an abstract Shape baseclass from which we can extend.

由于VBA不支持继承,我们*选择创建一个接口IShape,该接口保证某些属性/行为由泛型(正方形、圆形等)实现,而不是创建一个抽象的形状基类,我们可以从中扩展。

class IShape:

类IShape:

'Get the area of a shape
Public Function getArea() As Double
End Function

The part where we get in trouble is when we want to make every Shape drawable.
Unfortunately, since IShape is an interface and not a base class in VBA, we cannot implement the drawable interface in the base class. It appears that VBA does not allow us to have one interface implement another; after having tested this, the compiler doesn't seem to provide the desired behaviour. In other words, we cannot implement IDrawable within IShape, and expect instances of IShape to be forced to implement IDrawable methods because of this.
We are forced to implement this interface to every generic shape class that implements the IShape interface, and luckily VBA allows multiple interfaces to be implemented.

我们遇到麻烦的部分是当我们想让每个形状都可绘制时。不幸的是,由于IShape是一个接口,而不是VBA中的基类,所以我们无法在基类中实现drawable接口。看来VBA不允许我们有一个接口实现另一个接口;经过测试之后,编译器似乎没有提供所需的行为。换句话说,我们不能在IShape中实现IDrawable,因此我们期望IShape的实例*实现IDrawable方法。我们*将这个接口实现到每个实现IShape接口的泛型类,幸运的是VBA允许实现多个接口。

class cSquare:

类cSquare:

Option Explicit

Implements iShape
Implements IDrawable

Private pWidth          As Double
Private pHeight         As Double
Private pPositionX      As Double
Private pPositionY      As Double

Public Function iShape_getArea() As Double
    getArea = pWidth * pHeight
End Function

Public Function IDrawable_draw()
    debug.print "Draw square method"
End Function

'Getters and setters

The part that follows now is where the typical use / benefits of an interface come into play.

下面的部分是接口的典型使用/好处发挥作用的部分。

Let's start off our code by writing a factory that returns a new square. (This is just a workaround for our inability to send arguments directly to the constructor):

让我们从编写一个返回新正方形的工厂开始。(这只是我们无法直接向构造函数发送参数的一个解决方案):

module mFactory:

模块mFactory:

Public Function createSquare(width, height, x, y) As cSquare

    Dim square As New cSquare

    square.width = width
    square.height = height
    square.positionX = x
    square.positionY = y

    Set createSquare = square

End Function

Our main code will use the factory to create a new Square:

我们的主代码将使用工厂创建一个新的广场:

Dim square          As cSquare

Set square = mFactory.createSquare(5, 5, 0, 0)

When you look at the methods that you have at your disposal, you'll notice that you logically get access to all the methods that are defined on the cSquare class:

当您查看您可以使用的方法时,您将注意到您在逻辑上可以访问cSquare类中定义的所有方法:

如何在Excel VBA中使用工具

We'll see later on why this is relevant.

我们将在后面看到为什么这是相关的。

Now you should wonder what will happen if you really want to create a collection of drawable objects. Your app could happen to contain objects that aren't shapes, but that are yet drawable. Theoretically, nothing prevents you from having an IComputer interface that can be drawn (may be some clipart or whatever).
The reason why you might want to have a collection of drawable objects, is because you may want to render them in a loop at a certain point in the app lifecycle.

现在,如果您真的想创建一个可绘制对象集合,您应该想知道会发生什么。你的应用程序可能碰巧包含了不是形状的对象,但仍然是可绘制的。从理论上讲,没有什么可以阻止您拥有可以绘制的IComputer接口(可能是某个clipart或其他什么)。您可能希望拥有可绘制对象集合的原因是,您可能希望在应用程序生命周期的某个点将它们呈现在循环中。

In this case I will write a decorator class that wraps a collection (we'll see why). class collDrawables:

在本例中,我将编写包装集合的decorator类(我们将看到原因)。类collDrawables:

Option Explicit

Private pSize As Integer
Private pDrawables As Collection

'constructor
Public Sub class_initialize()
    Set pDrawables = New Collection
End Sub

'Adds a drawable to the collection
Public Sub add(cDrawable As IDrawable)
    pDrawables.add cDrawable

    'Increase collection size
    pSize = pSize + 1

End Sub

The decorator allows you to add some convenience methods that native vba collections don't provide, but the actual point here is that the collection will only accept objects that are drawable (implement the IDrawable interface). If we would try to add an object that is not drawable, a type mismatch would be thrown (only drawable objects allowed!).

decorator允许您添加一些本地vba集合不提供的便利方法,但这里的实际点是集合只接受可绘制的对象(实现IDrawable接口)。如果我们尝试添加一个不可绘制的对象,则会抛出一个类型不匹配(只允许绘制对象!)

So we might want to loop over a collection of drawable objects to render them. Allowing a non-drawable object into the collection would result in a bug. A render loop could look like this:

因此,我们可能希望对可绘制对象的集合进行循环以呈现它们。允许一个不可拖动的对象进入集合将导致错误。渲染循环如下:

Option Explicit

选项显式

Public Sub app()

    Dim obj             As IDrawable
    Dim square_1        As IDrawable
    Dim square_2        As IDrawable
    Dim computer        As IDrawable
    Dim person          as cPerson 'Not drawable(!) 
    Dim collRender      As New collDrawables

    Set square_1 = mFactory.createSquare(5, 5, 0, 0)
    Set square_2 = mFactory.createSquare(10, 5, 0, 0)
    Set computer = mFactory.createComputer(20, 20)

    collRender.add square_1
    collRender.add square_2
    collRender.add computer

    'This is the loop, we are sure that all objects are drawable! 
    For Each obj In collRender.getDrawables
        obj.draw
    Next obj

End Sub

Note that the above code adds a lot of transparency: we declared the objects as IDrawable, which makes it transparent that the loop will never fail, since the draw method is available on all objects within the collection.
If we would try to add a Person to the collection, it would throw a type mismatch if this Person class did not implement the drawable interface.

注意,上面的代码增加了很多透明度:我们声明对象为IDrawable,这使得循环不会失败变得透明,因为draw方法在集合中的所有对象上都是可用的。如果我们尝试将Person添加到集合中,如果这个Person类没有实现drawable接口,则会抛出类型不匹配。

But perhaps the most relevant reason why declaring an object as an interface is important, is because we only want to expose the methods that were defined in the interface, and not those public methods that were defined on the individual classes as we've seen before.

但是,将对象声明为接口的最相关的原因可能是,我们只想公开在接口中定义的方法,而不是像我们以前看到的那样在单个类上定义的那些公共方法。

Dim square_1        As IDrawable 

如何在Excel VBA中使用工具

Not only are we certain that square_1 has a draw method, but it also ensure that only methods defined by IDrawable get exposed.
For a square, the benefit of this might not be immediately clear, but let's have a look at an analogy from the Java collections framework that is much clearer.

我们不仅可以确定square_1有一个绘制方法,而且它还确保只有通过IDrawable定义的方法才能被公开。对于square来说,这样做的好处可能不会马上显现出来,但让我们看一下Java collections框架的一个类比,这个类比要清晰得多。

Imagine that you have a generic interface called IList that defines a set of methods applicable on different types of lists. Each type of list is a specific class that implements the IList interface, defining their own behaviour, and possibly adding more methods of their own on top.

假设您有一个名为IList的通用接口,它定义了一组适用于不同类型列表的方法。每种类型的列表都是一个特定的类,它实现IList接口,定义它们自己的行为,并可能在上面添加更多自己的方法。

We declare the list as follows:

我们声明如下:

dim myList as IList 'Declare as the interface! 

set myList = new ArrayList 'Implements the interface of IList only, ArrayList allows random (index-based) access 

In the above code, declaring the list as IList ensures that you won't use ArrayList-specific methods, but only methods that are prescribed by the interface. Imagine that you declared the list as follows:

在上面的代码中,将列表声明为IList可以确保您不会使用特定于arraylist的方法,而只使用接口指定的方法。假设您声明如下列表:

dim myList as ArrayList 'We don't want this

You will have access to the public methods that are specifically defined on the ArrayList class. Sometimes this might be desired, but often we just want to take benefit of the internal class behaviour, and not defined by the class specific public methods.
The benefit becomes clear if we use this ArrayList 50 more times in our code, and suddenly we find out that we're better off using a LinkedList (which allows specific internal behaviour related to this type of List).

您将可以访问ArrayList类中特定定义的公共方法。有时这可能是需要的,但通常我们只是想从内部的类行为中获益,而不是由类特定的公共方法定义。如果我们在代码中使用了这个ArrayList 50多次,那么我们就会发现,使用LinkedList(允许与此类列表相关的特定内部行为)会更好。

If we complied to the interface, we can change the line:

如果我们遵从界面,我们可以改变线条:

set myList = new ArrayList

to:

:

set myList = new LinkedList 

and none of the other code will break as the interface makes sure that the contract is fulfilled, ie. only public methods defined on IList are used, so the different types of lists are swappable over time.

当接口确保合同得以履行时,其他任何代码都不会中断。只使用在IList上定义的公共方法,所以不同类型的列表可以随时间变化。

A final thing (perhaps lesser known behaviour in VBA) is that you can give an interface a default implementation

最后一件事(VBA中较不为人知的行为)是,您可以为接口提供一个默认实现

We can define an interface in the following way:

我们可以这样定义一个接口:

IDrawable:

IDrawable:

Public Function draw()
    Debug.Print "Draw interface method"
End Function

and a class that implements the draw method as well:

以及实现绘制方法的类:

cSquare:

cSquare:

implements IDrawable 
Public Function draw()
    Debug.Print "Draw square method" 
End Function

We can switch between the implementations the following way:

我们可以通过以下方式在实现之间进行切换:

Dim square_1        As IDrawable

Set square_1 = New IDrawable
square_1.draw 'Draw interface method
Set square_1 = New cSquare
square_1.draw 'Draw square method    

This is not possible if you declare the variable as cSquare.
I can't immediately think of a good example when this might be useful, but it is technically possible if you test it.

如果将变量声明为cSquare,这是不可能的。我不能马上想到一个很好的例子,当这个可能有用的时候,但是如果您测试它,从技术上来说是可能的。

#3


10  

There are two undocumented additions about VBA and "Implements" statement.

关于VBA和“实现”语句,有两个没有文档说明的添加。

  1. VBA does not support undescore character '_' in a method name of an inherited interface of a derived class. F.e. it will not compile a code with method such as cShape.get_area (tested under Excel 2007): VBA will output the compile error above for any derived class.

    VBA不支持派生类继承接口的方法名中undescore字符“_”。它不会用cShape这样的方法编译代码。get_area(在excel2007下测试):VBA将为任何派生类输出上面的编译错误。

  2. If a derived class does not implement the own method named as in the interface, VBA compiles a code successfully, but the method will be inacessiable through a variable of the derived class type.

    如果派生类没有实现接口中命名的方法,VBA将成功编译代码,但是该方法将通过派生类类型的变量不可访问。

#4


8  

We must implement all methods of interface in the class which it is used.

我们必须实现它所使用的类中的所有接口方法。

cCircle Class

cCircle类

Option Explicit
Implements cShape

Public myRadius As Double

Public Function getDiameter()
    getDiameter = 2 * myRadius
End Function

Public Function getArea()
    getArea = Application.WorksheetFunction.Pi() * (myRadius ^ 2)
End Function

''Inertia around the X axis
Public Function getInertiaX()
    getInertiaX = Application.WorksheetFunction.Pi() / 4 * (myRadius ^ 4)
End Function

''Inertia around the Y axis
''Ix = Iy in a circle, technically should use same function
Public Function getIntertiaY()
    getIntertiaY = Application.WorksheetFunction.Pi() / 4 * (myRadius ^ 4)
End Function

Public Function toString()
    toString = "This is a radius " & myRadius & " circle."
End Function

Private Function cShape_getArea() As Variant

End Function

Private Function cShape_getInertiaX() As Variant

End Function

Private Function cShape_getIntertiaY() As Variant

End Function

Private Function cShape_toString() As Variant

End Function

cRectangle Class

cRectangle类

Option Explicit
Implements cShape

Public myLength As Double ''going to treat length as d
Public myWidth As Double ''going to treat width as b
Private getIntertiaX As Double

Public Function getArea()
    getArea = myLength * myWidth
End Function

Public Function getInertiaX()
    getIntertiaX = (myWidth) * (myLength ^ 3)
End Function

Public Function getIntertiaY()
    getIntertiaY = (myLength) * (myWidth ^ 3)
End Function

Public Function toString()
    toString = "This is a " & myWidth & " by " & myLength & " rectangle."
End Function

Private Function cShape_getArea() As Variant

End Function

Private Function cShape_getInertiaX() As Variant

End Function

Private Function cShape_getIntertiaY() As Variant

End Function

Private Function cShape_toString() As Variant

End Function

cShape Class

cShape类

Option Explicit

Public Function getArea()
End Function

Public Function getInertiaX()
End Function

Public Function getIntertiaY()
End Function

Public Function toString()
End Function

如何在Excel VBA中使用工具

#5


0  

Very interesting post to understand simply why and when an interface can be useful! But I think your final example about the default implementation is incorrect. The first call to the draw method of square_1 instantiated as IDrawable correctly prints the result you give, but the second call to the draw method of square_1 instantiated as cSquare is incorrect, nothing is printed. 3 different methods actually come into play:

非常有趣的文章,简单地理解为什么和什么时候界面可以有用!但是我认为关于默认实现的最后一个示例是不正确的。将square_1实例化为IDrawable的绘制方法的第一个调用正确地打印了您所给出的结果,但是将square_1的绘制方法实例化为cSquare的第二个调用是错误的,没有打印任何内容。实际上有三种不同的方法在起作用:

IDrawable.cls:

IDrawable.cls:

Public Function draw()
    Debug.Print "Interface Draw method"
End Function

cSquare.cls:

cSquare.cls:

Implements IDrawable

Public Function draw()
    Debug.Print "Class Draw method"
End Function

Public Function IDrawable_draw()
    Debug.Print "Interfaced Draw method"
End Function

Standard module:

标准模块:

Sub Main()
    Dim square_1 As Class6_Methods_IDrawable
    Set square_1 = New Class6_Methods_IDrawable
    Debug.Print "square_1 : ";
    square_1.draw

    Dim square_2 As Class6_Methods_cSquare
    Set square_2 = New Class6_Methods_cSquare
    Debug.Print "square_2 : ";
    square_2.draw 

    Dim square_3 As Class6_Methods_IDrawable
    Set square_3 = New Class6_Methods_cSquare
    Debug.Print "square_3 : ";
    square_3.draw
End Sub

Results in:

结果:

square_1 : Interface Draw method
square_2 : Class Draw method
square_3 : Interfaced Draw method

#6


0  

Quick Fix of Syntax

If the interface ISomeInterface has:

如果接口ISomeInterface有:

Public Sub someMethod()
    ' Interface, no code
End Sub

Then the implementation needs to be like:

然后实现需要如下:

Implements ISomeInterface

Public Sub ISomeInterface_someMethod()
    '      ^^^^^^^^^^^^^^^  ' If missing: Compile Error 
    ' Code goes here
End Sub

A nice approach:

一个很好的方法:

Implements ISomeInterface

Private Sub someMethod()
    ' Business logic goes here
End Sub

Public Sub ISomeInterface_someMethod()
    someMethod ' i.e. Business logic in 1 place: someMethod
End Sub

That said, the other answers are very much worth reading.

也就是说,其他的答案非常值得一读。