前面实现的将C#组件组合进VB工程中只是牛刀小试,现在我们来实现混合语言的面向对象编程(OOP),首先,我们来尝试一下混合语言组件的继承。
新建一个VB类库工程VBComponent,在工程中按上面的方法添加对CSharpClass.dll组件的引用,然后组件中增加一个新类:ExtendsFromCSharp,它继承自C#类CSharpClass,ExtendsFromCSharp类提供一个Add(x,y)方法,将两数相加,用UML表示如图4:
<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype>
图 4 从C#类继承而来的VB类
具体代码如下:
Imports CSharpClassNameSpace.CSharpClass
'继承自C#组件中的类CSharpClass
Public Class ExtendsFromCSharp
Inherits CSharpClassNameSpace.CSharpClass
Public Function Add(ByVal x As Integer, ByVal y As Integer) As Long
Return x + y
End Function
End Class
编译生成VBComponent.DLL组件(至于如何使用和测试这个类,就不用我多说了吧?)。
现在,VBComponent组件中的ExtendsFromCSharp类将拥有两个方法:SaySomething来自C#基类CSharpClass,而Add()函数则由VB实现。你创建的ExtendsFromCSharp类对象居然组合了两种不同语言开发的功能,你甚至不知道这些方法和函数是由哪种语言开发的,是不是很神奇?
让我们稍微休息一会,进行一个小结:
现在我们已掌握了从现成的组件中派生新类的方法,这就是说,我们可以建立自己的组件库,并在合适的时候动态地扩展它,这种扩展是跨语言的。
在VS.NET中使用混合语言组件时,当所使用的组件修改过后,注意需要动态地更新工程引用。方法是重新生成解决方案,或者是删除原有引用后再重新加入工程。
由于组件间相互继承,这就造成了组件库之间的依赖性(比如VBComponent.DLL组件就依赖于CSharpClass.DLL),因此,在实际项目开发中,任何一个项目小组成员所开发的组件库,都要求在文档中声明其组件库依赖性。另外,在系统开发完成部署到用户计算机上时,相互依赖的组件库必须绑在一起同时复制到用户计算机上,不能只提供一个。
下面,让我们走得更远一些,在组件间实现多态!
我们来实现一个“经典”的多态例子,它说明以下事实:圆和矩形都是几何图形,都可以求面积。
继续上面的例子,在C# 工程中选定原先的CSharpClass.cs文件,在类CSharpClass之外,输入以下代码:
public interface IMyShape
{
double Area();
}
public class CSharpRect :IMyShape
{
public float width;
public float height;
public double Area()
{
return width*height;
}
}
很明显,在这里我们定义了一个接口IMyShape,类CSharpRect实现了这个接口,它对接口所定义的求面积的函数double Area()提供了自己的实现:
矩形面积=长*宽
编译生成解决方案,CSharpClass.DLL得到更新。
打开VBComponent工程,注意到刷新此工程对CSharpClass.DLl的引用,检查方法是在选菜单:视图à对象浏览器,查看CSharpClass是否体现了新的更改,如5图所示:
图 5 检查组件是否更新
新建一个类文件VBCircle.vb,输入以下代码:
Public Class VBCircle
Implements CSharpClassNameSpace.IMyShape
Private _radius As Single
Property Radius() As Single
Get
Radius = _radius
End Get
Set(ByVal Value As Single)
_radius = Value
End Set
End Property
Public Sub New()
_radius = 0
End Sub
Public Function Area() As Double Implements CSharpClassNameSpace.IMyShape.Area
Return System.Math.PI * _radius * _radius
End Function
End Class
编译,生成VBComponent.DLL。
现在,我们就实现了基于组件的以下面向对象系统设计:
新建一个使用VB的Windows应用程序,在工程中加入对以上两个组件库的引用,往窗体上加入一个按钮,在其Click事件中书写以下代码:
Private Sub Button_Click(…) Handles Button6.Click
Dim VBObj As New VBComponent.VBCircle()
VBObj.Radius =10
Dim CSharpObj As New CSharpClassNameSpace.CSharpRect()
Dim area As Double
CSharpObj.width = 10
CSharpObj.height = 20
CalArea(VBObj)
CalArea(CSharpObj)
End Sub
其中CalArea()是一个Sub过程,它接收一个IMyShape类型的参数,可以传给它任何一个实现了IMyShape接口的对象,这种以接口而不是具体类来编定代码的方法,就是典型的多态编码方式。
Private Sub CalArea(ByVal obj As CSharpClassNameSpace.IMyShape)
MessageBox.Show(Convert.ToString(obj.Area()))
End Sub
经过测试,跨语言、跨组件的多态的确可以正常地动作。
好了,现在让我们好好想想都实现了什么东西,可以怎样应用到开发实践中:
我们可以将某个接口封装到一个组件中,然后,具体实现这个接口的类我们可以放到不同的组件中,交给不同的程序员以他们所喜爱的语言来开发。使用这些组件的程序员只要在他的代码中始终使用接口变量而不是具体类的变量,我们就可以动态地替换掉某个具体的实现接口的组件,而不会影响使用这些组件的程序。
哇,这不就意味着我们可以把一个完整的软件系统给大卸八块,然后根据具体情况选一个组件插入系统?太棒了!下面,就让我们来开始进行组件化编程最精彩之旅---软件组件的动态装配与插拔。