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

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

问题描述

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

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?

背景信息: 近年来,由于谷歌的 Eric Lippert 先生和大量的测试/实验,我对协变和逆变的处理能力不断提高.在我正在进行的项目中,我需要将架构的不同层分开,而不是将基本模型/实体类型暴露给更高层(表示).为了实现这一点,我一直在创建复合类(MVC 模型),其中包含潜在的多种不同基础层模型类型的方面.我有一个单独的层,它将从基本类型(服务层)构建这些复合类型.一个重要的要求是基类型不能通过引用传递,因此必须复制属性以创建基模型类的深层副本.

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.

为了从服务层中删除一些冗长而丑陋的代码,我创建了一个接口,该接口为复合类型定义了一个通用契约,允许将属性值复制到复合对象中.但是,当我想多次实现此接口时,VB 编译器会生成警告.该程序运行良好,但我想了解发生这种情况的具体原因.特别是,如果这是一个脆弱或糟糕的设计决策,我想在深入了解之前立即了解.

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.

环境详情:

  • 语言:VB.Net
  • .NET: 4.0
  • IDE:VS2010 SP1
  • 用途:网站 (MVC2)
  • Language: VB.Net
  • .NET: 4.0
  • IDE: VS2010 SP1
  • Usage: Website (MVC2)

为了解决这个问题,我在 SO 和互联网上做了一些研究,但没有什么能真正解决我的问题.以下是我查阅过的一些(但不是全部)资源:

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:

假设 MaterialB 继承了 MaterialA,然后又被 SpecialB 继承了

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

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

结合

Public Interface ICopiesFrom(Of In TModel)

说(由于 'In'):Composite 可以是 ICopiesFrom(Of <anything Inheriting from MaterialA>)(有一个实现)和Composite 可以是 ICopiesFrom(Of <anything Inheriting from MaterialB>)(带有第二个实现)

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)

所以如果我说:

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

它应该选择哪个实现,两者都是有效的(选择B似乎很自然,但有歧义)

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??

在您的示例中也没有任何内容需要 In 关键字(也许在实际代码中可能有).除非您需要传递" Composite AS a ICopiesFrom(Of SpecialB) 例如您一无所获,否则 ICopiesFrom(Of MaterialB) 可以处理 SpecialB 而没有 (通用)逆变,通过正常(非通用)机制.

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