背景信息:近年来,由于Eric Lippert先生,谷歌以及数小时的测试/实验,我对协方差和逆变的处理一直在增加.在我正在开发的项目中,我需要分离架构的不同层,而不是将基础模型/实体类型暴露给更高层(表示).为了实现这一点,我一直在创建复合类(MVC模型),其中包含潜在的多个不同基础层模型类型的方面.我有一个单独的层,将从基本类型(服务层)构建这些复合类型.一个重要的要求是基类型不能通过引用传递,因此必须复制属性才能创建基本模型类的深层副本.
为了从服务层中删除一些冗长而丑陋的代码,我创建了一个接口,该接口定义了复合类型的通用契约,允许在复合对象中复制属性值.但是,当我想多次实现此接口时,VB编译器会生成警告.程序运行得很好,但我想了解为什么会发生这种情况的具体细节.特别是,如果这是一个脆弱或糟糕的设计决定,我想在我变得太深之前就知道了.
环境细节:
>语言:VB.Net
> .NET:4.0
> IDE:VS2010 SP1
>用法:网站(MVC2)
在试图解决这个问题时,我已经对SO和互联网做了一些研究,但没有真正解决我的问题.以下是我咨询过的一些(但不是全部)资源:
> Contravariance explained
> http://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
> http://ericlippert.com/2013/07/29/a-contravariance-conundrum/#more-1344
简介:有没有更好/更清晰/更灵活的方式来实现我正在尝试或者我必须忍受编译器警告?
以下是说明问题的可运行示例(不是实际代码):
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
因为(接口)和/或具有预先存在的继承层次结构的类的(通用)逆变而存在警告.它没有特别适用于您在示例中给出的情况(可能在您的实际代码中),但这就是它警告的原因:
假设MaterialB继承了MaterialA,然后由SpecialB继承
Public Class Composite Implements ICopiesFrom(Of MaterialA) Implements ICopiesFrom(Of MaterialB)
结合
Public Interface ICopiesFrom(Of In TModel)
说(由于’In’):
Composite可以是ICopiesFrom(Of<任何继承自MaterialA的东西>)(有一个实现)
和
Composite可以是ICopiesFrom(Of<任何继承自MaterialB的东西>)(第二个实现)
所以,如果我说:
Dim broken As ICopiesFrom(Of SpecialB) = New Composite()
它应该选择哪种实现,两者都是有效的(选择B似乎很自然,但它是模棱两可的)
接口可能更清楚的情况:
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()
编译器现在应该使用哪种实现?
此外,您的示例中没有任何内容需要In关键字(可能在实际代码中可能存在).除非你需要’传递’复合AS作为ICopiesFrom(Of SpecialB),例如你什么都没有,ICopiesFrom(Of MaterialB)可以通过普通(非通用)机制处理没有(通用)逆变的SpecialB.