重载决议适用于普通方法但不适用于构造函数

Overload resolution works for normal method but not for constructor

我的目标是进行一系列重载,其中根据参数类型(仅在 运行 时间已知)调用正确版本的方法。但是,在我要重载的方法是构造函数的情况下,我 运行 遇到了一个有趣的问题。

采用如下继承结构:

Public MustInherit Class A
    Public Property Common As String
End Class

Public Class X
    Inherits A

    Public Property Unique1 As String
    Public Property Unique2 As String
End Class

Public Class Y
    Inherits A

    Public Property Unique3 As String
    Public Property Unique4 As String
End Class

Base class AXY 继承。

现在用这个 class 来说明问题:

Public Class Foo
    Public Sub New(v As X)
        Common = v.Common
        Prop1 = v.Unique1
        Prop2 = v.Unique2
        Prop3 = "Some value"
        Prop3 = String.Empty
    End Sub

    Public Sub New(v As Y)
        Common = v.Common
        Prop1 = "Some value"
        Prop2 = String.Empty
        Prop3 = v.Unique3
        Prop4 = v.Unique4
    End Sub

    Public ReadOnly Property Common As String
    Public ReadOnly Property Prop1 As String
    Public ReadOnly Property Prop2 As String
    Public ReadOnly Property Prop3 As String
    Public ReadOnly Property Prop4 As String

    Public Shared Sub Bar(v As X)
    End Sub

    Public Shared Sub Bar(v As Y)
    End Sub
End Class

有一个带有重载的普通方法 Bar,还有一个带有重载的构造函数 New。第一个 New 与第一个 Bar 具有相同的签名,第二个 New 与第二个 Bar.

具有相同的签名

最后拿下这个测试码:

Public Sub Test()
    Dim Param As Object = New X

    'This works fine
    Foo.Bar(Param)

    'This gives a compile error
    Dim Thing As New Foo(Param)
End Sub

编译器似乎对 Bar 的调用没有问题,但是对于构造函数调用,我得到以下编译错误:

Overload resolution failed because no accessible 'New' can be called without a narrowing conversion:
'Public Sub New(v As X)': Argument matching parameter 'v' narrows from 'Object' to 'X'.
'Public Sub New(v As Y)': Argument matching parameter 'v' narrows from 'Object' to 'Y'.

为什么构造函数调用会导致错误,而对 Bar 的调用不会。

此外,如果我将 Param 声明更改为 Dim Param As A = New X,那么它们都不会编译。

我觉得我应该理解这一点,但出于某种原因我不理解。有人可以告诉我为什么这不起作用,并可能建议解决方法吗?

虽然目前还不清楚您要实现的目标,但答案是共享代码的唯一合理位置。这是尝试使用 Option Strict On 解决您的问题,使用接口来定义 class 必须具有的属性,以便传递给 Foo 进行构造。

注意代码中的注释,它们也有助于解释事情。

这对事物进行了抽象,因此 Foo 不必知道从 A 派生的所有类型 - 它只知道接口。事实上,它 'inverts' 这种关系使得 A 及其派生类型知道 Foo 需要什么(根据接口)。剩下的是 X 和 Y 的实现,其中 Props 1 到 4 的定义现在存在(而不是在各种重载的 Foo 构造函数中)。这会将 Foo 的构造函数数量缩减为一个。

将 class 派生自 A 的属性转换为 Foo 的属性的逻辑必须存在于 某处 。通过将此逻辑推出 Foo 而不是派生的 classes,您可以避免 Option Strict Off 延迟到运行时的缩小问题。另外,从A派生出一个新的Zclass很容易,不用修改Foo就可以立即使用

但是同样,由于您并不完全清楚您打算使用此示例代码做什么,所以很难知道这种方法 'works' 是否适合您的想法。

Option Strict On

Module Module1
    Sub Main()
        Dim Param As A = New X
        Dim Thing As New Foo(Param)

        Param = New Y
        Thing = New Foo(Param)

        'if you make a new class Z which Inherits A, it will immediately be translatable to Foo
        'albeit with all String.Empty properties unless you override the properties from A
    End Sub

End Module

'Defines what a Foo wants, what a Foo needs
Public Interface IPropertiesForFoo
    ReadOnly Property Common As String
    ReadOnly Property Prop1 As String
    ReadOnly Property Prop2 As String
    ReadOnly Property Prop3 As String
    ReadOnly Property Prop4 As String
End Interface

Public MustInherit Class A
    Implements IPropertiesForFoo

    Public Property Common As String


#Region "IPropertiesForFoo implementation"
    'these are Overridable, so derived classes can choose what to change and what not to
    'note these are all Protected, so only derived classes know about them.  Users of A may not care.

    'This is just one choice;
    ' you could also use Throw New NotImplementedException (instead of Return String.Empty)
    ' and force derived classes to handle every property

    Protected Overridable ReadOnly Property IPropertiesForFoo_Prop1 As String Implements IPropertiesForFoo.Prop1
        Get
            Return String.Empty
        End Get
    End Property

    Protected Overridable ReadOnly Property IPropertiesForFoo_Prop2 As String Implements IPropertiesForFoo.Prop2
        Get
            Return String.Empty
        End Get
    End Property

    Protected Overridable ReadOnly Property IPropertiesForFoo_Prop3 As String Implements IPropertiesForFoo.Prop3
        Get
            Return String.Empty
        End Get
    End Property

    Protected Overridable ReadOnly Property IPropertiesForFoo_Prop4 As String Implements IPropertiesForFoo.Prop4
        Get
            Return String.Empty
        End Get
    End Property

    'private, and doesn't need to be Overridable, as Common can map directly
    Private ReadOnly Property IPropertiesForFoo_Common As String Implements IPropertiesForFoo.Common
        Get
            Return Common
        End Get
    End Property
#End Region
End Class

Public Class X
    Inherits A

    Public Property Unique1 As String
    Public Property Unique2 As String

    Protected Overrides ReadOnly Property IPropertiesForFoo_Prop1 As String
        Get
            Return Unique1
        End Get
    End Property

    Protected Overrides ReadOnly Property IPropertiesForFoo_Prop2 As String
        Get
            Return Unique2
        End Get
    End Property

    Protected Overrides ReadOnly Property IPropertiesForFoo_Prop3 As String
        Get
            Return "Some value"
        End Get
    End Property

    'doesn't need to override Prop4; leave it as String.Empty
End Class

Public Class Y
    Inherits A

    Public Property Unique3 As String
    Public Property Unique4 As String

    Protected Overrides ReadOnly Property IPropertiesForFoo_Prop1 As String
        Get
            Return "Some value"
        End Get
    End Property

    'doesn't need to override Prop2; leave it as String.Empty

    Protected Overrides ReadOnly Property IPropertiesForFoo_Prop3 As String
        Get
            Return Unique3
        End Get
    End Property

    Protected Overrides ReadOnly Property IPropertiesForFoo_Prop4 As String
        Get
            Return Unique4
        End Get
    End Property
End Class


Public Class Foo

    Public Sub New(v As IPropertiesForFoo)
        Common = v.Common
        Prop1 = v.Prop1
        Prop2 = v.Prop2
        Prop3 = v.Prop3
        Prop4 = v.Prop4
    End Sub

    Public ReadOnly Property Common As String
    Public ReadOnly Property Prop1 As String
    Public ReadOnly Property Prop2 As String
    Public ReadOnly Property Prop3 As String
    Public ReadOnly Property Prop4 As String


End Class

就此而言,根据 Foo 的其余部分实际执行的操作,您甚至可能不需要 Foo - 只需传递 A 的实例即可,因为它们也是IPropertiesForFoo。然后根据需要拉出标记为 Prop1、Prop2 的属性。 (同样,您的简化示例源没有充分暗示更大的上下文,无法知道这种方法是否适合。)

我敢肯定会有很多人再次告诉我我不应该这样做,但这是我根据需要实际解决问题的方法:

Public Class Foo
    Public Sub New(v As X)
    End Sub

    Public Sub New(v As Y)
    End Sub

    Public Shared Function Create(v As X) As Foo
        Return New Foo(v)
    End Function

    Public Shared Function Bar(v As Y) As Foo
        Return New Foo(v)
    End Function
End Class

这让我可以像这样使用 Foo

Dim Param As Object = New Y
Foo.Create(Param)

是的,上面使用了后期绑定和松散类型的代码。但它也将冗余代码保持在最低限度,不需要冗长的接口定义或实现,仍然是完全可预测的,并且完全符合我的要求。当在正确的上下文中使用时,我确实认为这样的功能是有用和有效的。

我仍然希望我能得到一些关于为什么重载决议适用于普通方法而不适用于构造函数的答案。但现在我想我只需要解决这个问题。