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 notShapesCollection.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并导航到该文件。
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 classesNB:您需要先将代码保存在.cls文件中,然后导入它,因为VBEditor不允许您使用属性。属性允许您在迭代中指定默认成员,并在自定义集合类上使用for each循环。
See more:
看到更多:
- Chris Pearson: Default Member
- 克里斯·皮尔森:默认成员
-
Daily Dose of Excel: Custom Collection Class
Excel的日剂量:自定义收集类
-
Excel Forum: Attribute Statements of VBA Classes
Excel论坛:VBA类的属性语句
-
PC-Review: VBA Excel Collections
PC-Review:Excel VBA集合
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
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 methoddraw
). 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类中定义的所有方法:
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
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和“实现”语句,有两个没有文档说明的添加。
-
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将为任何派生类输出上面的编译错误。
-
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
#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 notShapesCollection.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并导航到该文件。
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 classesNB:您需要先将代码保存在.cls文件中,然后导入它,因为VBEditor不允许您使用属性。属性允许您在迭代中指定默认成员,并在自定义集合类上使用for each循环。
See more:
看到更多:
- Chris Pearson: Default Member
- 克里斯·皮尔森:默认成员
-
Daily Dose of Excel: Custom Collection Class
Excel的日剂量:自定义收集类
-
Excel Forum: Attribute Statements of VBA Classes
Excel论坛:VBA类的属性语句
-
PC-Review: VBA Excel Collections
PC-Review:Excel VBA集合
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
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 methoddraw
). 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类中定义的所有方法:
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
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和“实现”语句,有两个没有文档说明的添加。
-
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将为任何派生类输出上面的编译错误。
-
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
#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.
也就是说,其他的答案非常值得一读。