【问题标题】:Storing an object that implements multiple interfaces and derives from a certain base (.net)存储实现多个接口并从某个基础(.net)派生的对象
【发布时间】:2011-05-21 08:53:49
【问题描述】:

在 .net 中,可以使用泛型,以便函数可以接受支持一个或多个接口并从基类型派生的参数,即使不存在任何可以派生所有有效参数类型的单一类型。例如,可以说:

Sub Foo(Of T As {IInterface1, IInterface2, SomeBaseType})(Param as T)

并允许传递实现 IInterface1 和 IInterface2 的 SomeBaseType 的任何派生类。即使 SomeBaseType 不支持 Interface1 和 Interface2,这也可以工作,并且实现这些接口的类不共享任何同样实现它们的共同祖先。

如果在函数退出后不需要将参数保留在任何地方,这将非常方便。不幸的是,我想不出一种方法来持久化传入的参数,以便以后可以将其传递给类似的函数,除非可能使用反射。有什么好的方法吗?

我能想到的最接近的方法是定义一个接口 INest(也许不是最好的名字——任何人都可以改进它吗?)因此:

Interface INest(Of Out T)
    Function Nest() As T
End Interface

对于将与其他接口或基类“约束”结合使用的任何接口,请定义一个通用版本,如下所示

Interface IFun1
    ' Any members of the interface go here, e.g. ...'
    Sub DoFun1()
End Interface

Interface IFun1(Of Out T)
    ' This one does nothing but inherit'
    Inherits IFun1, INest(Of T)
End Interface

一个支持多个接口的类应该将自己声明为实现泛型接口,并将自己作为类型参数。

Class test123a
    Inherits sampleBase
    Implements IFun1(Of test123a), IFun2(Of test123a), IFun3(Of test123a)
End Class

如果这样做,可以定义一个支持多个约束的函数参数或类变量:

Dim SomeField as IFun1(Of IFun2(Of IFun3(Of sampleBase)))

然后将任何派生自 sampleBase 的类分配给它,该类实现了这些接口。 SomeField 将实现 IFun1; SomeField.Nest 将实现 IFun2; SomeField.Nest.Nest 将实现 IFun3。请注意,除了从 INest(Of T) 继承的通用接口之外,IFun1、IFun2、IFun3 或 sampleBase 不需要共享任何公共派生。另请注意,无论一个类实现了多少 INest 派生接口,它只需要定义一个 INest(Of T).Nest 实现即可。

不是很漂亮,但它有两个好处:(1) 任何实际上实现了必要接口的具体类都可以直接分配给如上所述声明的字段,而无需进行类型转换; (2) 虽然以不同顺序链接类型的字段不兼容赋值,但它们可以相互类型转换。

有没有更好的方法来存储某些东西,使其“已知”支持多个接口并从某个基本类型派生?鉴于可以以类型安全的方式编写这样的代码,如果编译器提供一点帮助,.net 2.0 CLR 似乎可以很好地支持这样的事情。不过,我不知道目前的编译器有什么特别好的方法。

【问题讨论】:

    标签: vb.net inheritance interface .net-2.0 covariance


    【解决方案1】:

    我能想到的最好的方法是做一个抽象的存储和这个存储的通用实现。例如(请原谅我的 VB.NET):

    MustInherit Class Storage
        Public MustOverride Sub DoSomething()
    End Class
    
    Class Storage(Of T As {IInterface1, IInterface2, SomeBaseType})
        Inherits Storage
    
        Public Overrides Sub DoSomething()
            ' do something with Value.
        End Sub
    
        Public Value As T
    End Class
    

    及用法

    Dim S As Storage
    
    Sub Foo(Of T As {IInterface1, IInterface2, SomeBaseType})(ByVal Param As T)
        S = New Storage(Of T) With {.Value = Param}
    End Sub
    
    Sub UseS()
        S.DoSomething();
    End Sub
    

    更新:好的,因为我们可能无法提前识别所有操作:

    MustInherit Class Storage
        MustOverride ReadOnly Property SomeBaseType As SomeBaseType
        MustOverride ReadOnly Property IInterface1 As IInterface1
        MustOverride ReadOnly Property IInterface2 As IInterface2
    End Class
    
    Class Storage(Of T As {IInterface1, IInterface2, SomeBaseType})
        Inherits Storage
    
        Public Value As T
    
        Public Overrides ReadOnly Property IInterface1 As IInterface1
            Get
                Return Value
            End Get
        End Property
    
        Public Overrides ReadOnly Property IInterface2 As IInterface2
            Get
                Return Value
            End Get
        End Property
    
        Public Overrides ReadOnly Property SomeBaseType As SomeBaseType
            Get
                Return Value
            End Get
        End Property
    End Class
    

    【讨论】:

    • 如果可以提前识别出所有可能想要对对象执行的操作,则此方法有效。也许会有一种不太可怕的方式将它与部分类一起使用,因此可以在它们被调用的位置附近定义不同的做某事例程,但代码最终可能会过于冗长。有时 C 预处理器是邪恶的,但有时它绝对可以帮助管理冗长。
    猜你喜欢
    • 2017-02-26
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    • 2020-04-22
    • 1970-01-01
    相关资源
    最近更新 更多