重载决议适用于普通方法但不适用于构造函数
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 A
被 X
和 Y
继承。
现在用这个 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
派生出一个新的Z
class很容易,不用修改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)
是的,上面使用了后期绑定和松散类型的代码。但它也将冗余代码保持在最低限度,不需要冗长的接口定义或实现,仍然是完全可预测的,并且完全符合我的要求。当在正确的上下文中使用时,我确实认为这样的功能是有用和有效的。
我仍然希望我能得到一些关于为什么重载决议适用于普通方法而不适用于构造函数的答案。但现在我想我只需要解决这个问题。
我的目标是进行一系列重载,其中根据参数类型(仅在 运行 时间已知)调用正确版本的方法。但是,在我要重载的方法是构造函数的情况下,我 运行 遇到了一个有趣的问题。
采用如下继承结构:
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 A
被 X
和 Y
继承。
现在用这个 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
派生出一个新的Z
class很容易,不用修改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)
是的,上面使用了后期绑定和松散类型的代码。但它也将冗余代码保持在最低限度,不需要冗长的接口定义或实现,仍然是完全可预测的,并且完全符合我的要求。当在正确的上下文中使用时,我确实认为这样的功能是有用和有效的。
我仍然希望我能得到一些关于为什么重载决议适用于普通方法而不适用于构造函数的答案。但现在我想我只需要解决这个问题。