VB.NET实现多种逆变接口类型 [英] VB.NET Implement Multiple Contravariant interface types

查看:186
本文介绍了VB.NET实现多种逆变接口类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本问题:给定一个接口:ICopiesFrom(Of TModel)中泛型参数没有类型约束的情况下,该接口是否可以在同一具体类型上多次使用不同的实现没有编译器警告的类型参数?



背景信息:由于Eric Lippert先生,近年来我对协方差和逆变的处理一直在增加,谷歌和许多小时的测试/实验。在我正在开发的一个项目中,我需要分离体系结构的不同层,而不要将基础模型/实体类型公开到更高层(演示文稿)。为了实现这一点,我一直在创建包含潜在多个不同基础层模型类型的方面的复合类(MVC模型)。我有一个单独的图层,将从基础类型(服务层)构建这些复合类型。一个重要的要求是基本类型不能通过引用传递,因此必须复制属性才能创建基础模型类的深层副本。



为了从服务层中删除一些冗长和丑陋的代码,我创建了一个接口,该接口为复合类型定义了一个公共约定,允许在复合对象中复制属性值。但是,当我想要多次实现此接口时,VB编译器会生成警告。该程序运行得很好,但我想了解为什么会发生这种情况的具体细节。特别是,如果这是一个脆弱或糟糕的设计决策,我现在就想知道,然后再深入。

环境细节:


  • 语言: VB.Net

  • .NET: 4.0
  • >
  • IDE: VS2010 SP1

  • 用法:网站(MVC2) b


为了解决这个问题,我在SO和互联网上做了一些研究,但没有真正解决我的问题。以下是我所咨询的一些资源(但不是全部):


  • 解释变形

  • http://msdn.microsoft.com/en-us/library/dd799517(v = vs.110).aspx

  • http://ericlippert.com / 2013/07/29 / a-contravariance-conundrum /#more-1344

    $ b

    摘要:是否有更好/更清洁/更灵活的方法来实现我正在尝试做的或者我必须忍受编译器警告?



    这是一个可运行的示例(不是实际的代码)说明了问题:

     公共模块材料

    Sub Main()
    Dim materials As New List(Of Composite)()
    Dim materialData As New Dictionary(Of MaterialA,MaterialB)()

    '从数据源加载数据
    'materialData = Me.DataService.Load(.....'查询参数'.....)
    Dim specificMaterial As New SpecialB()With {.Weight = 24,.Height = 12}
    Dim specificMaterialDesc As New MaterialA()With {.Name =Silly Putty ,.Created = DateTime.UtcNow.AddDays(-1)}
    Dim basicMaterial As New MaterialB()With {.Weight = 34.2,.Height = 8}
    Dim basicMaterialDesc As New MaterialA()With { .name =Gak,.Created = DateTime.UtcNow.AddDays(-2)}

    materialData.Add(specificMaterialDesc,specificMaterial)
    materialData.Add(basicMaterialDesc,basicMaterial)

    对于每个项目在材料数据
    Dim newMaterial As新Composite()

    newMaterial.CopyFrom(item.Key)
    newMaterial.CopyFrom(item.Value)
    materials.Add(newM材料)



    Console.WriteLine(Total Weight:{0} lbs。,materials.Select(Function(x)x.Weight).Sum() )
    Console.ReadLine()
    End Sub

    End Module


    '''< summary>
    '''表示两个独立类组成的类。
    '''< / summary>
    '''<备注>< /备注>
    公共类复合
    实现ICopiesFrom(Of MaterialA)
    实现ICopiesFrom(Of MaterialB)

    #Region--Constants--

    Private Const COMPOSITE_PREFIX As String =Comp_

    #End Region

    #Region--Instance Variables--

    私有_created作为DateTime
    私人_height作为双
    私人_name作为字符串
    私人_weight作为双

    #End地区

    #Region - 构造函数 -

    '''< summary>
    '''创建Composite类的新实例。
    '''< / summary>
    '''<备注>< /备注>
    Public Sub New()
    _created = DateTime.MaxValue
    _height = 1D
    _name = String.Empty
    _weight = 1D
    End Sub

    #End Region

    #Region--Methods--

    公共覆盖重载Sub CopyFrom(ByVal模型作为MaterialA)实现ICopiesFrom(Of MaterialA) .CopyFrom
    如果模型IsNot Nothing Then
    Me.Name = model.Name
    Me.Created = model.Created
    End If
    End Sub

    公共覆盖重载Sub CopyFrom(ByVal模型作为MaterialB)实现ICopiesFrom(Of MaterialB).CopyFrom
    如果模型IsNot Nothing Then
    Me.Height = model.Height
    Me.Weight =减重
    结束如果
    结束小组

    #结束区域

    #区域 - 功能 -

    受保护的Overridable函数GetName()As String
    Dim returnValue As String = String.Empty
    If不是String.IsNullOrWhiteSpace(Me.Name)然后
    返回String.Concat(COMPOSITE_PREFIX,Me.Name)
    End If
    返回returnValue
    End Function

    #End Region

    #Region--Properties--

    公共重写属性创建为DateTime
    获取
    返回_created
    结束获取
    设置(值为DateTime)
    _created =值
    结束设置
    结束属性

    公开可重写属性高度
    获取
    返回_height
    结束获取
    设置(值为双倍)
    如果值> 0D然后
    _height = value
    结束如果
    结束集
    结束属性

    公共可重写属性名称作为字符串
    获取
    返回Me.GetName()
    结束获取
    Set(value As String)
    如果不是String.IsNullOrWhiteSpace(value)则
    _name = value
    End If
    结束设置
    完成属性

    公共可重写属性权重双倍
    获取
    返回_weight
    结束获取
    Set(value As Double )
    如果值> 0D然后
    _weight = value
    End If
    End Set
    End Property

    #End Region

    End Class

    '''< summary>
    '''暴露合约的接口/定义其值来自另一种类型的类型的功能。
    '''< / summary>
    '''< typeparam name =TModel>< / typeparam>
    '''<备注>< /备注>
    公共接口ICopiesFrom(Of TModel)

    #Region--Methods--

    '''< summary>
    '''将给定的模型复制到当前实例中。
    '''< / summary>
    '''< param name =model>< / param>
    '''<备注>< /备注>
    Sub CopyFrom(ByVal model As TModel)

    #End Region

    结束接口

    公共类MaterialA

    #Region--Instance Variables--

    私人_create As DateTime
    私人_name作为字符串

    #End Region

    #Region--Constructors--

    '''< summary>
    '''创建MaterialA类的新实例。
    '''< / summary>
    '''<备注>< /备注>
    Public Sub New()
    _created = DateTime.MaxValue
    _name = String.Empty
    End Sub

    #End Region

    #Region--Properties--

    Public Overridable Property创建为DateTime
    获取
    返回_created
    结束获取
    Set(value As DateTime)
    _created = value
    结束设置
    完成属性

    公共可重写属性名称作为字符串
    获取
    返回_name
    结束获取
    Set(value As String)
    _name = value
    End Set
    End Property

    #End Region

    End Class

    Public Class MaterialB

    #Region--Instance Variables--

    Private _height Double
    Private _weight As Double

    #End Region

    #Region--Constructors--

    '''< summary>
    '''创建MaterialB类的新实例。
    '''< / summary>
    '''<备注>< /备注>
    Public Sub New()
    _height = 0D
    _weight = 0D
    End Sub

    #End Region

    #Region --Properties--

    公共可重写属性高度为双
    获得
    返回_height
    最终获得
    设置(值为双)
    _height = value
    结束设置
    结束属性

    公共可重写属性权重双重
    获取
    返回_weight
    结束获取
    Set(value As Double)
    _weight = value
    End Set
    End Property

    #End Region

    End Class

    公共类SpecialB
    继承材质B

    公开覆盖物业权重双倍
    获得
    返回MyBase.Weight
    结束获得
    Set(value As Double)
    MyBase.Weight = value * 2
    End Set
    End Property

    End Class


    解决方案


    该程序运行得很好,但我想了解为什么发生这种情况的具体细节


    警告在那里因为(通用)与接口和/或具有预先存在的继承层次的类相反。它并不特别适用于你在你的例子中给出的情况(可能在你的真实代码中),但这是为什么它会发出警告:

    假设MaterialB Inherited MaterialA然后被SpecialB继承

     公共类Composite 
    实现ICopiesFrom(Of MaterialA)
    Implements ICopiesFrom(Of MaterialB)

    加上

    <$公共接口ICopiesFrom(Of TModel)

    到'In'):
    Composite可以是 ICopiesFrom(Of <任何东西继承自MaterialA>)(带有一个实现)
    AND
    Composite可以是 ICopiesFrom(Of <任何继承MaterialB的东西>)(带有第二个实现)

    因此,如果我说:

      Dim broken As ICopiesFrom(Of SpecialB)= New Composite()

    应该选择哪种实现方式,都是有效(选择B似乎很自然,但它是不明确的)



    情况如果接口可能更清楚:

     公共类Composite2 
    实现ICopiesFrom(Of IMaterialA)
    实现ICopiesFrom(Of IMaterialB)
    ...
    公共类Broken
    实现IMaterialA
    实现IMaterialB
    ...
    Dim dis As ICopiesFrom(Of Broken)= New Composite()

    编译器现在应该使用哪种实现? In关键字(也许可能存在真实代码)。除非你需要'传递'复合AS作为 ICopiesFrom(Of SpecialB),例如你没有获得任何东西, ICopiesFrom(Of MaterialB) code>可以通过正常(非通用)机制来处理没有(通用)逆转的SpecialB。


    Basic Question: Given an interface: ICopiesFrom(Of In TModel) where there is no type constraint on the generic argument, can that interface be implemented more than once on the same concrete type using a different type argument without a compiler warning?

    Background Info: My handle on covariance and contravariance has been increasing in recent years thanks to Mr. Eric Lippert, Google, and many hours of testing / experimenting. In a project I am working on, I have a need to separate different layers of the architecture and not expose base model / entity types to a higher layer (presentation). To accomplish this, I have been creating composite classes (MVC Models) that contain aspects of potentially multiple different base layer model types. I have a separate layer that will build these composite types from the base types (service layer). One important requirement is that the base types not be passed up via a reference, so properties must be duplicated in order to create a deep-copy of the base model class.

    To remove some of the lengthy and ugly code from the service layer, I created an interface that defines a common contract for composite types that allows for the property values to be copied in the composite object. When I want to implement this interface multiple times however, the VB compiler generates a warning. The program runs just fine, but I want to understanding the specifics of why this is happening. Particularly, if this is a fragile or poor design decision, I want to know now before I get too deep.

    Environment Details:

    • Language: VB.Net
    • .NET: 4.0
    • IDE: VS2010 SP1
    • Usage: Website (MVC2)

    In attempting to figure this out, I have done some reasearch on SO and the internet but nothing really addresses my question specifically. Here are some of (but not all) the resources I have consulted:

    Summary: Is there a better / cleaner / more flexible way to achieve what I'm trying to or do I have to live with the compiler warning?

    Here is a run-able example (not the actual code) that illustrates the issue:

    Public Module Materials
    
        Sub Main()
            Dim materials As New List(Of Composite)()
            Dim materialData As New Dictionary(Of MaterialA, MaterialB)()
    
            'Load data from a data source
            'materialData = Me.DataService.Load(.....'Query parameters'.....)
            Dim specificMaterial As New SpecialB() With {.Weight = 24, .Height = 12}
            Dim specificMaterialDesc As New MaterialA() With {.Name = "Silly Putty", .Created = DateTime.UtcNow.AddDays(-1)}
            Dim basicMaterial As New MaterialB() With {.Weight = 34.2, .Height = 8}
            Dim basicMaterialDesc As New MaterialA() With {.Name = "Gak", .Created = DateTime.UtcNow.AddDays(-2)}
    
            materialData.Add(specificMaterialDesc, specificMaterial)
            materialData.Add(basicMaterialDesc, basicMaterial)
    
            For Each item In materialData
                Dim newMaterial As New Composite()
    
                newMaterial.CopyFrom(item.Key)
                newMaterial.CopyFrom(item.Value)
                materials.Add(newMaterial)
    
            Next
    
            Console.WriteLine("Total Weight: {0} lbs.", materials.Select(Function(x) x.Weight).Sum())
            Console.ReadLine()
        End Sub
    
    End Module
    
    
    ''' <summary>
    ''' Class that represents a composite of two separate classes.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class Composite
        Implements ICopiesFrom(Of MaterialA)
        Implements ICopiesFrom(Of MaterialB)
    
    #Region "--Constants--"
    
        Private Const COMPOSITE_PREFIX As String = "Comp_"
    
    #End Region
    
    #Region "--Instance Variables--"
    
        Private _created As DateTime
        Private _height As Double
        Private _name As String
        Private _weight As Double
    
    #End Region
    
    #Region "--Constructors--"
    
        ''' <summary>
        ''' Creates a new instance of the Composite class.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()
            _created = DateTime.MaxValue
            _height = 1D
            _name = String.Empty
            _weight = 1D
        End Sub
    
    #End Region
    
    #Region "--Methods--"
    
        Public Overridable Overloads Sub CopyFrom(ByVal model As MaterialA) Implements ICopiesFrom(Of MaterialA).CopyFrom
            If model IsNot Nothing Then
                Me.Name = model.Name
                Me.Created = model.Created
            End If
        End Sub
    
        Public Overridable Overloads Sub CopyFrom(ByVal model As MaterialB) Implements ICopiesFrom(Of MaterialB).CopyFrom
            If model IsNot Nothing Then
                Me.Height = model.Height
                Me.Weight = model.Weight
            End If
        End Sub
    
    #End Region
    
    #Region "--Functions--"
    
        Protected Overridable Function GetName() As String
            Dim returnValue As String = String.Empty
            If Not String.IsNullOrWhiteSpace(Me.Name) Then
                Return String.Concat(COMPOSITE_PREFIX, Me.Name)
            End If
            Return returnValue
        End Function
    
    #End Region
    
    #Region "--Properties--"
    
        Public Overridable Property Created As DateTime
            Get
                Return _created
            End Get
            Set(value As DateTime)
                _created = value
            End Set
        End Property
    
        Public Overridable Property Height As Double
            Get
                Return _height
            End Get
            Set(value As Double)
                If value > 0D Then
                    _height = value
                End If
            End Set
        End Property
    
        Public Overridable Property Name As String
            Get
                Return Me.GetName()
            End Get
            Set(value As String)
                If Not String.IsNullOrWhiteSpace(value) Then
                    _name = value
                End If
            End Set
        End Property
    
        Public Overridable Property Weight As Double
            Get
                Return _weight
            End Get
            Set(value As Double)
                If value > 0D Then
                    _weight = value
                End If
            End Set
        End Property
    
    #End Region
    
    End Class
    
    ''' <summary>
    ''' Interface that exposes a contract / defines functionality of a type whose values are derived from another type.
    ''' </summary>
    ''' <typeparam name="TModel"></typeparam>
    ''' <remarks></remarks>
    Public Interface ICopiesFrom(Of In TModel)
    
    #Region "--Methods--"
    
        ''' <summary>
        ''' Copies a given model into the current instance.
        ''' </summary>
        ''' <param name="model"></param>
        ''' <remarks></remarks>
        Sub CopyFrom(ByVal model As TModel)
    
    #End Region
    
    End Interface
    
    Public Class MaterialA
    
    #Region "--Instance Variables--"
    
        Private _created As DateTime
        Private _name As String
    
    #End Region
    
    #Region "--Constructors--"
    
        ''' <summary>
        ''' Creates a new instance of the MaterialA class.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()
            _created = DateTime.MaxValue
            _name = String.Empty
        End Sub
    
    #End Region
    
    #Region "--Properties--"
    
        Public Overridable Property Created As DateTime
            Get
                Return _created
            End Get
            Set(value As DateTime)
                _created = value
            End Set
        End Property
    
        Public Overridable Property Name As String
            Get
                Return _name
            End Get
            Set(value As String)
                _name = value
            End Set
        End Property
    
    #End Region
    
    End Class
    
    Public Class MaterialB
    
    #Region "--Instance Variables--"
    
        Private _height As Double
        Private _weight As Double
    
    #End Region
    
    #Region "--Constructors--"
    
        ''' <summary>
        ''' Creates a new instance of the MaterialB class.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()
            _height = 0D
            _weight = 0D
        End Sub
    
    #End Region
    
    #Region "--Properties--"
    
        Public Overridable Property Height As Double
            Get
                Return _height
            End Get
            Set(value As Double)
                _height = value
            End Set
        End Property
    
        Public Overridable Property Weight As Double
            Get
                Return _weight
            End Get
            Set(value As Double)
                _weight = value
            End Set
        End Property
    
    #End Region
    
    End Class
    
    Public Class SpecialB
        Inherits MaterialB
    
        Public Overrides Property Weight As Double
            Get
                Return MyBase.Weight
            End Get
            Set(value As Double)
                MyBase.Weight = value * 2
            End Set
        End Property
    
    End Class
    

    解决方案

    The program runs just fine, but I want to understanding the specifics of why this is happening

    The warning is there because of (Generic) contravariance with respect to Interfaces and/or classes with an pre-existing inheritance hierarchy. It doesn't specifically apply in the case you have given in your example (may in your real code), but here is why it is warning:

    suppose MaterialB Inherited MaterialA and was in turn Inherited by SpecialB then

    Public Class Composite
    Implements ICopiesFrom(Of MaterialA)
    Implements ICopiesFrom(Of MaterialB)
    

    combined with

    Public Interface ICopiesFrom(Of In TModel)
    

    says (due to the 'In'): Composite can be an ICopiesFrom(Of <anything Inheriting from MaterialA>) (with one implementation) AND Composite can be an ICopiesFrom(Of <anything Inheriting from MaterialB>) (with second implementation)

    so if I say:

    Dim broken As ICopiesFrom(Of SpecialB) = New Composite()
    

    Which implementation should it choose, both are valid (it seems natural to choose B, but it is ambiguous)

    the Situation if perhaps more clear with Interfaces:

    Public Class Composite2
    Implements ICopiesFrom(Of IMaterialA)
    Implements ICopiesFrom(Of IMaterialB)
    ...
    Public Class Broken
    Implements IMaterialA
    Implements IMaterialB
    ...
    Dim broken As ICopiesFrom(Of Broken) = New Composite()
    

    Which implementation should the compiler use now??

    Also there is nothing in you example that Requires the In keyword (perhaps there may be in the real code). Unless you need to 'pass around' Composite AS a ICopiesFrom(Of SpecialB) for example you are gaining nothing, the ICopiesFrom(Of MaterialB) can cope with a SpecialB without (Generic) contravariance, via the normal (non-generic) mechanisms.

    这篇关于VB.NET实现多种逆变接口类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆