Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net Net继承和成员可见性存在奇怪的问题_.net_Inheritance_Oop - Fatal编程技术网

.net Net继承和成员可见性存在奇怪的问题

.net Net继承和成员可见性存在奇怪的问题,.net,inheritance,oop,.net,Inheritance,Oop,我在VB.Net类库中遇到了一个问题,我将其简化为以下内容 Public MustInherit Class TargetBase End Class Public Class TargetOne Inherits TargetBase End Class Public Class TargetTwo Inherits TargetBase End Class Public Class TargetManager Public Sub UpdateTargets(

我在VB.Net类库中遇到了一个问题,我将其简化为以下内容

Public MustInherit Class TargetBase

End Class

Public Class TargetOne
    Inherits TargetBase
End Class

Public Class TargetTwo
    Inherits TargetBase
End Class

Public Class TargetManager
    Public Sub UpdateTargets(ByVal Targets As List(Of TargetBase))
        For Each objTarget As TargetBase In Targets
            UpdateTarget(objTarget)
        Next
    End Sub

    Private Sub UpdateTarget(ByVal Value As TargetOne)

    End Sub

    Private Sub UpdateTarget(ByVal Value As TargetTwo)

    End Sub
End Class
由于
UpdateTarget(objTarget)
行上的语法错误,这将无法编译-重载解析失败,因为如果不进行缩小转换,就无法调用可访问的“UpdateTarget”

所以我改变了For Each循环使用Object而不是TargetBase

For Each objTarget As Object In Targets
    UpdateTarget(objTarget)
Next
现在可以编译了,但我得到一个运行时错误-找不到类型为“TargetManager”的公共成员“UpdateTarget”

因此,我采取了明显的下一步,将2个UpdateTarget()重载设置为公共(而不是私有)

现在可以了

我几乎可以理解为什么将其更改为Object会起作用,但为什么在我仅从同一个类中调用这些方法时必须公开这些方法?我宁愿它们在这个类之外不可用

有人能解释一下吗

提前感谢(对于这个问题的长度,我深表歉意!)

附加的 谢谢大家的回答。我已经找到了解决方法(使UpdateTarget方法公开)使它能够工作。另一个解决方法是在调用UpdateTarget之前对objTarget进行类型检查,然后进行DirectCast,如

For Each objTarget As Object In Targets
    If TypeOf objTarget Is TargetOne Then
        UpdateTarget(DirectCast(objTarget, TargetOne))
    ElseIf TypeOf objTarget Is TargetTwo Then
        UpdateTarget(DirectCast(objTarget, TargetTwo))
    End If
Next
这也会起作用-我发布了这个问题,因为我真的很想理解为什么将UpdateTarget的可见性从Private更改为Public会消除运行时错误,这完全违背了我的理解

在我看来,它无法决定使用什么方法,因为您使用的是两个方法参数的基本类型。TargetOne/Two都是有效的targetBase,因此这两种方法在解析引擎看来是相同的,这意味着它无法拾取

然而,我不知道为什么其他的改变使它工作。。。让我想想,更新待定

在C#中,我不明白这个问题,因为你不能将TargetBase向前投射到TargetOne或TargetTwo。。。它给出了不同的编译器错误-方法的参数无效,因为它无法隐式地将基转换为派生。您提到的第一个编译器错误基本上是与VB.NET等效的

我发现了这个链接,但我不确定它是针对VB还是VB.NET的-无论哪种方式,读起来都很有趣:

这也可能与VB.NET 2010中的
选项严格
和协方差有关。本文有一个小的重载部分,可能会很有用:

更新:请注意,我不知道它为什么会突然起作用,这听起来像是针对Jon Skeet或Eric Lippert的

更新2:我可以建议针对每种情况(私有到公共/对象的使用)编译应用程序并使用Reflector查看IL。基本上,查找任何差异——可能是编译器在后台为您添加了一些东西——或者是,或者运行时能够根据当前类型确定正确的方法

更新3:我想我明白了。以下链接中的这句话:

“一个对象在被绑定时是早绑定的 分配给声明为 特定对象类型的

说明当您指定TargetBase时,它是早期绑定的,编译器会抱怨。当您指定对象时,它是后期绑定的,并且运行时在其私有对象重新绑定此链接时会发出抱怨:


因此,为您指定公共工程。运行时显然能够延迟绑定到正确的重载—VB.NET为您隐藏了延迟绑定的一个很好的功能:-)

正如Adam所说,编译器不知道应该调用哪个方法。但是,这看起来像是
UpdateTarget
方法应该是每个目标类型覆盖的实例方法。通过这种方式,您可以迭代列表,只需在
objTarget
上调用
UpdateTarget


这样做的另一个优点是可以更好地封装代码
TargetManager
不需要知道更新的实际功能,只需要调用它即可。此外,在编写
TargetThree
的过程中,您不必更改
TargetManager
来更新新类型。

更新:因为您在评论中指出,在您的情况下不可能使用正常的多态方法来解决此问题,我至少强烈建议将您的
TargetManager
类更改为具有一个接受
TargetBase
参数的
updateTargetGet
方法。然后检查该方法中的适当类型。这防止了潜在的问题,有

If TypeOf x Is A Then
    DoSomething(DirectCast(x, A))
ElseIf TypeOf x Is B Then
    DoSomething(DirectCast(x, B))
End If
…到处都是

换句话说,把那张难看的支票放在一个地方:

Public Class TargetManager
    Public Sub UpdateTarget(ByVal target As TargetBase)
        Dim t1 = TryCast(target, TargetOne)
        If t1 IsNot Nothing Then
            UpdateTargetOne(t1)
            Return
        End If

        Dim t2 = TryCast(target, TargetTwo)
        If t2 IsNot Nothing Then
            UpdateTargetTwo(t2)
            Return
        End If
    End Sub

    ' I would also recommend changing the targets parameter type here '
    ' to IEnumerable(Of TargetBase), as that is all you need to do '
    ' a For Each loop. '
    Public Sub UpdateTargets(ByVal targets As IEnumerable(Of TargetBase))
        For Each objTarget As TargetBase In Targets
            UpdateTarget(objTarget)
        Next
    End Sub

    Private Sub UpdateTargetOne(ByVal target As TargetOne)
        ' Do something. '
    End Sub

    Private Sub UpdateTargetTwo(ByVal target As TargetTwo)
        ' Do something. '
    End Sub
End Class

你倒过来了

一开始,我直觉地认为你真的希望这样做:

Public MustInherit Class TargetBase
    Protected Friend MustOverride Sub Update()
End Class

Public Class TargetOne
    Inherits TargetBase

    Protected Friend Overrides Sub Update()
    End Sub
End Class

Public Class TargetTwo
    Inherits TargetBase

    Protected Friend Overrides Sub Update()
    End Sub
End Class

Public Class TargetManager
    Public Sub UpdateTargets(ByVal Targets As List(Of TargetBase))
        For Each objTarget As TargetBase In Targets
            objTarget.Update()
        Next
    End Sub
End Class

虽然VB不是我的专长,但我想我能正确回答你的问题

在程序的第一个版本中,您有
UpdateTarget(objTarget)
,其中objTarget是TargetBase类型。理由如下:

  • 电话的接受者是“我”。它有一个著名的编译时类型TargetManager
  • 调用的参数是“objTarget”。它有一个著名的编译时类型TargetBase
  • 因为接收方和参数都有类型,所以我们应该执行重载解析来确定要调用哪个版本的UpdateTarget
  • 重载解析确定UpdateTarget的两个版本都需要从TargetBase到更特定类型的潜在不安全转换
  • 因此,重载解析失败
在第二个版本中,objTarget是Object类型。理由如下

  • 调用的参数的类型为Object
  • 再一次,超负荷解决方案在这里对我们没有任何好处
  • 由于重载解析失败,并且某个对象类型为,并且Option Strict未启用,因此生成执行分析的代码
    Public MustInherit Class TargetBase
        Protected Friend MustOverride Sub Update()
    End Class
    
    Public Class TargetOne
        Inherits TargetBase
    
        Protected Friend Overrides Sub Update()
        End Sub
    End Class
    
    Public Class TargetTwo
        Inherits TargetBase
    
        Protected Friend Overrides Sub Update()
        End Sub
    End Class
    
    Public Class TargetManager
        Public Sub UpdateTargets(ByVal Targets As List(Of TargetBase))
            For Each objTarget As TargetBase In Targets
                objTarget.Update()
            Next
        End Sub
    End Class
    
    public void UpdateTargets(IEnumerable<TargetBase> targets) 
    {
        foreach(var targetOne in targets.OfType<TargetOne>())
            UpdateTarget(targetOne);
        foreach(var targetTwo in targets.OfType<TargetTwo>())
            UpdateTarget(targetTwo);
    }