是否可以将接口添加到现有类型?

时间:2014-05-21 11:54:34

标签: vb.net

我为我的类型开发了一些序列化代码。它们共享一个通用接口以促进序列化逻辑。是否可以将该接口添加到某些基本类型,如IntegerString,这样我就可以将其中一个基本类型值传递给我的序列化逻辑并让它工作?我想象扩展方法的一些东西,但添加一个接口而不是一个方法?我确定我可以通过某种方式来设法使用后期绑定,但如果可能的话,我想避免这种情况。

1 个答案:

答案 0 :(得分:2)

没有。无法扩展现有类型以使其实现接口(不能将Implements IMyInterface添加到该类型的代码顶部,即)。最接近的是创建一个派生类,它将接口添加到基类。如果覆盖CType运算符,您甚至可以创建它,以便可以将值从一种类型无缝转换为另一种类型而无需显式转换它们。但是,由于您提到了StringInteger作为要扩展的类型,因此甚至无法实现。您无法创建继承自String的新类型,因为String定义为NotInheritable。同样,您无法创建继承自Integer的新类型,因为IntegerStructure,而不是Class。结构不支持继承。

因此,您拥有的最佳选择是创建一个包装核心值的新类,通过实现接口扩展它,然后覆盖CType运算符,以便在核心之间进行转换类型和包装类型。例如,让我们说你有这样的界面:

Public Interface IWritable
    Sub Write()
End Interface

你有一个方法接受了这种类型的论证,如下:

Private Sub TestWrite(writableObject As IWritable)
    writableObject.Write()
End Sub

如果你需要将Integer传递给该方法,你可以创建一个这样的包装类:

Public Class WritableInteger
    Implements IWritable

    Public Sub New(value As Integer)
        Me.Value = value
    End Sub

    Public Property Value As Integer

    Public Sub Write() Implements IWritable.Write
        Console.Write(Value)
    End Sub

    Overloads Shared Widening Operator CType(value As Integer) As WritableInteger
        Return New WritableInteger(value)
    End Operator

    Overloads Shared Widening Operator CType(value As WritableInteger) As Integer
        Return value.Value
    End Operator
End Class

由于CType运算符重载为Widening,这意味着您可以在不转换的情况下转换两种类型之间的值(即使使用Option Strict On)。例如,这有效:

Dim w As WritableInteger = New WritableInteger(5)
Dim i As Integer = w
w = i

很遗憾,由于TestWrite要求的是IWritable而不是WritableInteger,因此您只能使用TestWrite致电Integer,像这样:

Dim i As Integer = 5
TestWrite(5)  'This won't work!

编译器知道它需要将Integer转换为IWritable对象,但由于任意数量的类型都可以实现该接口,因此它不会自动尝试找出是否存在他们为此提供CType运算符。由于可能有多种类型允许将转化范围从Integer扩展到IWritable,因此它只会引起人们的愤怒。因此,即使声明了扩展转换,您仍然必须在类似的情况下显式转换类型。例如:

Dim i As Integer = 5
TestWrite(CType(i, WritableInteger))

或者,或许更简单:

Dim i As Integer = 5
TestWrite(New WritableInteger(5))

通过为需要包装的所有常见类型创建重载,可以使其更加方便。例如,如果您为TestWrite方法创建了一个重载,如下所示:

Public Sub TestWrite(value As Integer)
    TestWrite(New WritableInteger(value))
End Sub

然后你可以轻松地这样称呼它:

TestWrite(5)

但是,从IWritable变量转换回Integer更加困难。例如:

Dim w As IWritable = New WritableInteger(5)
Dim i As Integer = w  ' This won't work!
Dim i2 As Integer = CType(w, Integer)  ' Whis won't work either!

如果你需要这样做,你实际上必须首先将它转换为WritableInteger(并且首先知道它是那种类型的对象),例如:

Dim w As IWritable = New WritableInteger(5)
If TypeOf w Is WritableInteger Then
    Dim i As Integer = CType(w, WritableInteger)
End If

不幸的是,在保持编译时类型检查的安全性的同时,实际上没有办法使这更容易。

最后还值得一提的是,如果您决定制作这样的包装器,并且无论包装类型如何,接口的实现都是相同的,那么您可以将其实现为泛型类型,如这样:

Public Class Writable(Of T)
    Implements IWritable

    Public Sub New(value As T)
        Me.Value = value
    End Sub

    Public Property Value As T

    Public Sub Write() Implements IWritable.Write
        Console.Write(Value)
    End Sub

    Overloads Shared Widening Operator CType(value As T) As Writable(Of T)
        Return New Writable(Of T)(value)
    End Operator

    Overloads Shared Widening Operator CType(value As Writable(Of T)) As T
        Return value.Value
    End Operator
End Class

然后你可以像这样调用TestWrite方法:

TestWrite(New Writable(Of Integer)(5))
TestWrite(New Writable(Of String)("Hello World"))