xDa*_*vax 8 .net vb.net architecture generics contravariance
基本问题: 给定一个接口:ICopiesFrom(Of TModel),对泛型参数没有类型约束,是否可以在不使用编译器警告的情况下使用不同的类型参数在同一具体类型上多次实现该接口?
背景信息:近年来,由于Eric Lippert先生,Google以及数小时的测试/实验,我对协方差和逆变的处理一直在增加.在我正在开发的项目中,我需要分离架构的不同层,而不是将基础模型/实体类型暴露给更高层(表示).为了实现这一点,我一直在创建复合类(MVC模型),其中包含潜在的多个不同基础层模型类型的方面.我有一个单独的层,将从基本类型(服务层)构建这些复合类型.一个重要的要求是基类型不能通过引用传递,因此必须复制属性才能创建基本模型类的深层副本.
为了从服务层中删除一些冗长而丑陋的代码,我创建了一个接口,该接口定义了复合类型的通用契约,允许在复合对象中复制属性值.但是,当我想多次实现此接口时,VB编译器会生成警告.程序运行得很好,但我想了解为什么会发生这种情况的具体细节.特别是,如果这是一个脆弱或糟糕的设计决定,我想在我变得太深之前就知道了.
环境细节:
在试图解决这个问题时,我已经对SO和互联网进行了一些研究,但没有具体解决我的问题.以下是我咨询过的一些(但不是全部)资源:
简介:有没有更好/更清洁/更灵活的方式来实现我正在尝试或不必使用编译器警告?
以下是说明问题的可运行示例(不是实际代码):
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
Run Code Online (Sandbox Code Playgroud)
该程序运行得很好,但我想了解发生这种情况的具体原因
之所以出现该警告,是因为与具有预先存在的继承层次结构的接口和/或类存在(通用)逆变。它并不特别适用于您在示例中给出的情况(可能在您的真实代码中),但这就是它发出警告的原因:
假设MaterialB继承了MaterialA并且又被SpecialB继承了,那么
Public Class Composite
Implements ICopiesFrom(Of MaterialA)
Implements ICopiesFrom(Of MaterialB)
Run Code Online (Sandbox Code Playgroud)
结合
Public Interface ICopiesFrom(Of In TModel)
Run Code Online (Sandbox Code Playgroud)
说(由于“In”):复合可以是ICopiesFrom(Of <anything Inheriting from MaterialA>)(使用一个实现)并且复合可以是ICopiesFrom(Of <anything Inheriting from MaterialB>)(使用第二个实现)
所以如果我说:
Dim broken As ICopiesFrom(Of SpecialB) = New Composite()
Run Code Online (Sandbox Code Playgroud)
它应该选择哪个实现,两者都有效(似乎很自然地选择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()
Run Code Online (Sandbox Code Playgroud)
编译器现在应该使用哪种实现?
此外,您的示例中没有任何内容需要In 关键字(也许真实代码中可能有)。除非您需要“传递”Composite AS aICopiesFrom(Of SpecialB)例如,您什么也得不到,否则ICopiesFrom(Of MaterialB)可以通过正常(非通用)机制处理没有(通用)逆变的 SpecialB。
| 归档时间: |
|
| 查看次数: |
331 次 |
| 最近记录: |