VB.NET实现复式逆变接口类型 [英] VB.NET Implement Mutliple Contravariant interface types

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

问题描述

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

背景信息:我的协方差和逆变手柄不断增加,近年来由于埃里克利珀先生,谷歌和许多小时的测试/实验的。在一个项目中我的工作,我有必要分开架构的不同层,而不是暴露示范基地/实体类型为高层(presentation)。要做到这一点,我一直在创造复合类(MVC模型),包含潜在的多个不同的底层模型类型的各个方面。我有一个单独的层,将建立这些复合类型的基本类型(服务层)。一个重要的要求是,碱的类型不能通过参考向上传递,所以属性必须以创建基础模型类的深拷贝复制

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

环境细节:

  • 语言: VB.Net
  • .NET: 4.0
  • IDE: VS2010 SP1
  • 用法:网站(MVC2)

在试图弄清楚这一点,我已经做了所以一些reasearch和互联网,但没有什么专门解决我的问题。这里有一些(但不是全部),我曾咨询资源:

  • 逆变解释
  • <一个href="http://msdn.microsoft.com/en-us/library/dd799517%28v=vs.110%29.aspx">http://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
  • <一个href="http://ericlippert.com/2013/07/29/a-contravariance-conundrum/#more-1344">http://ericlippert.com/2013/07/29/a-contravariance-conundrum/#more-1344

摘要:有没有更好/清洁/更灵活的方式来达到什么我想还是我住的编译器警告

下面是示出这一问题的运行能够例子(不是实际的$ C $三):

 公共模块材料

    副主()
        昏暗的材料作为新的列表(复合)()
        昏暗materialData作为新词典(MaterialA,MaterialB)()

        从数据源加载数据
        materialData = Me.DataService.Load(.....查询参数......)
        昏暗specificMaterial作为新SpecialB()有{。重量= 24,.Height = 12}
        昏暗specificMaterialDesc作为新MaterialA(){随着请将.Name =橡皮泥,.Created = DateTime.UtcNow.AddDays(-1)}
        昏暗basicMaterial作为新MaterialB()有{。重量= 34.2,.Height = 8}
        昏暗basicMaterialDesc作为新MaterialA(){随着请将.Name =GAK,.Created = DateTime.UtcNow.AddDays(-2)}

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

        每个项目在materialData
            昏暗新型材料作为新型复合()

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

        下一个

        Console.WriteLine(总重量:{​​0}磅,materials.Select(功能(X)x.Weight).SUM())
        到Console.ReadLine()
    结束小组

前端模块


'''&LT;总结&gt;
'''是重新presents的两个独立的类复合类。
'''&LT; /总结&gt;
'''&LT;说明&GT;&LT; /说明&GT;
公共类组合
    实现ICopiesFrom(中MaterialA)
    实现ICopiesFrom(中MaterialB)

#REGION--Constants--

    私人常量COMPOSITE_ preFIX的String =Comp_

#END地区

#REGION--instance Variables--

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

#END地区

#REGION--Constructors--

    '''&LT;总结&gt;
    '''创建复合类的新实例。
    '''&LT; /总结&gt;
    '''&LT;说明&GT;&LT; /说明&GT;
    公共子新()
        _created = DateTime.MaxValue
        _height = 1D
        _name =的String.Empty
        _weight = 1D
    结束小组

#END地区

#REGION--Methods--

    公众可重写重载子的copyfrom(BYVAL模型作为MaterialA)实现ICopiesFrom(中MaterialA).CopyFrom
        如果模型状态并没有任何然后
            Me.Name = model.Name
            Me.Created = model.Created
        结束如果
    结束小组

    公众可重写重载子的copyfrom(BYVAL模型作为MaterialB)实现ICopiesFrom(中MaterialB).CopyFrom
        如果模型状态并没有任何然后
            Me.Height = model.Height
            Me.Weight = model.Weight
        结束如果
    结束小组

#END地区

#REGION--Functions--

    受保护的可重写功能的GetName()作为字符串
        昏暗的returnValue作为字符串=的String.Empty
        如果不String.IsNullOrWhiteSpace(Me.Name)然后
            返回String.Concat(COMPOSITE_ preFIX,Me.Name)
        结束如果
        返回的returnValue
    端功能

#END地区

#REGION--Properties--

    公众可覆盖属性创建为DAT​​ETIME
        得到
            返回_created
        最终获取
        套装(价值为DATETIME)
            _created =价值
        结束设定
    高端物业

    公众可覆盖属性的高度,双
        得到
            返回_height
        最终获取
        套装(价值双人间)
            如果值GT; 0D然后
                _height =价值
            结束如果
        结束设定
    高端物业

    公众可覆盖属性名称作为字符串
        得到
            返回Me.GetName()
        最终获取
        设置(值作为字符串)
            如果不String.IsNullOrWhiteSpace(值),
                _name =价值
            结束如果
        结束设定
    高端物业

    公众可覆盖属性权重作为双
        得到
            返回_weight
        最终获取
        套装(价值双人间)
            如果值GT; 0D然后
                _weight =价值
            结束如果
        结束设定
    高端物业

#END地区

末级

'''&LT;总结&gt;
'''接口暴露合同/定义了一个类型,其值可以从另一种类型的衍生功能。
'''&LT; /总结&gt;
'''&LT; typeparam名=TModel的&GT;&LT; / typeparam&GT;
'''&LT;说明&GT;&LT; /说明&GT;
公共接口ICopiesFrom(中的TModel)

#REGION--Methods--

    '''&LT;总结&gt;
    '''复制一个给定的模型到当前实例。
    '''&LT; /总结&gt;
    '''&LT; PARAM NAME =模式和GT;&LT; /参数&GT;
    '''&LT;说明&GT;&LT; /说明&GT;
    子的copyfrom(BYVAL模型作为的TModel)

#END地区

结束接口

公共类MaterialA

#REGION--instance Variables--

    私人_created为DATETIME
    私人_name作为字符串

#END地区

#REGION--Constructors--

    '''&LT;总结&gt;
    '''创建MaterialA类的新实例。
    '''&LT; /总结&gt;
    '''&LT;说明&GT;&LT; /说明&GT;
    公共子新()
        _created = DateTime.MaxValue
        _name =的String.Empty
    结束小组

#END地区

#REGION--Properties--

    公众可覆盖属性创建为DAT​​ETIME
        得到
            返回_created
        最终获取
        套装(价值为DATETIME)
            _created =价值
        结束设定
    高端物业

    公众可覆盖属性名称作为字符串
        得到
            返回_name
        最终获取
        设置(值作为字符串)
            _name =价值
        结束设定
    高端物业

#END地区

末级

公共类MaterialB

#REGION--instance Variables--

    私人_height作为双
    私人_weight作为双

#END地区

#REGION--Constructors--

    '''&LT;总结&gt;
    '''创建MaterialB类的新实例。
    '''&LT; /总结&gt;
    '''&LT;说明&GT;&LT; /说明&GT;
    公共子新()
        _height = 0D
        _weight = 0D
    结束小组

#END地区

#REGION--Properties--

    公众可覆盖属性的高度,双
        得到
            返回_height
        最终获取
        套装(价值双人间)
            _height =价值
        结束设定
    高端物业

    公众可覆盖属性权重作为双
        得到
            返回_weight
        最终获取
        套装(价值双人间)
            _weight =价值
        结束设定
    高端物业

#END地区

末级

公共类SpecialB
    继承MaterialB

    公共覆盖物业重量双
        得到
            返回MyBase.Weight
        最终获取
        套装(价值双人间)
            MyBase.Weight =值* 2
        结束设定
    高端物业

末级
 

Pleaes让我知道如果有什么我可以澄清或任何其他细节,将是有益的。

解决方案
  

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

的警告是因为(通用)逆变相对于接口和/或类具有$ P $对现有的继承层次结构存在。它没有专门适用于你给你的榜样的情况下(可能会在您的真实code),但在这里就是为什么它被警告:

假设MaterialB继承MaterialA并反过来由SpecialB继承的话

 公共类组合
实现ICopiesFrom(中MaterialA)
实现ICopiesFrom(中MaterialB)
 

结合

 公共接口ICopiesFrom(中的TModel)
 

说(由于在'): 复合材料可以是 ICopiesFrom(中&LT;任何从MaterialA&GT继承;)(有一个实现) 和 复合材料可以是 ICopiesFrom(中&LT;任何从MaterialB&GT继承;)(与第二个执行)

所以,如果我说:

 暗淡打破由于ICopiesFrom(中SpecialB)=新型复合()
 

执行哪个应该是选择,都是有效的(他很自然地选择B,但它是不明确的)

势若也许更清晰,接口:

 公共类Composite2
实现ICopiesFrom(中IMaterialA)
实现ICopiesFrom(中IMaterialB)
...
残破的公共类
实现IMaterialA
实现IMaterialB
...
DIM打破由于ICopiesFrom(碎)=新型复合()
 

执行哪个现在应该编译器使用??

也没有什么在你例子的需要的in关键字(也许有可能是在现实code)。除非你需要'绕过'复合为一个 ICopiesFrom(中SpecialB)比如你正在获得什么,在 ICopiesFrom <(中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

Pleaes let me know if there is anything I can clarify or any additional detail that would be helpful.

解决方案

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天全站免登陆