【问题标题】:Why can't I access Internal/Friend properties from a dynamic object?为什么我不能从动态对象访问内部/朋友属性?
【发布时间】:2018-04-23 11:52:22
【问题描述】:

我将首先展示 VB.NET 代码,因为它的 C# 等效代码的行为更加令人困惑(见下文)

考虑以下三个类:

Public Class BaseClass
    Private Shared Rand As New Random
    Public Shared Function CreateDerived() As BaseClass
        Return If(Rand.Next(1, 3) = 1, New DerivedClass1(), New DerivedClass2())
    End Function
End Class

Public Class DerivedClass1
    Inherits BaseClass

    Sub New()
        MyProperty = 1
    End Sub

    Friend Property MyProperty As Integer
End Class

Public Class DerivedClass2
    Inherits BaseClass

    Sub New()
        MyProperty = 2
    End Sub

    Friend Property MyProperty As Integer
End Class

现在,当我尝试做这样的事情时:

Sub Foo()
    Dim targetClass As BaseClass = BaseClass.CreateDerived()

    Dim Casted
    If TypeOf (targetClass) Is DerivedClass1 Then
        Casted = DirectCast(targetClass, DerivedClass1)
    ElseIf TypeOf (targetClass) Is DerivedClass2 Then
        Casted = DirectCast(targetClass, DerivedClass2)
    Else
        Exit Sub
    End If

    Console.WriteLine(Casted.MyProperty) 'Throws an exception.
End Sub

我似乎无法访问MyProperty,并且收到以下异常:

找不到类型“DerivedClass1”的公共成员“MyProperty”。

所以,当我将MyProperty 的访问级别更改为Public 时,代码按预期工作。

奇怪的是,当我在 VS 2015 上尝试上述代码的 C# 等效代码时,它工作得很好,但是 在 .NET Fiddler 上,它没有。

Here's the C# example 在 .NET Fiddler 上我得到与 VB.NET 相同的行为。

那么,是不是我做错了什么?

【问题讨论】:

  • 您在 Foo 方法中的转换是没有意义的,因为您没有将结果分配给该类型的变量。如果你这样做,你可能会看到你想要的行为:Console.WriteLine(DirectCast(targetClass, DerivedClass1).MyProperty)
  • 您的 C# 和 VB 代码不等效,因为 C# 使用 VB 没有的 dynamic。您的 Casted 变量是 VB 中的 Object 类型。
  • 大概,VB 中的后期绑定仅适用于Public 成员。我想这是有道理的,因为后期绑定实际上涉及由框架内的代码访问的成员,这意味着在声明该成员的程序集之外,因此 Friend 成员将无法访问。 C# 的dynamic 不同,大概不涉及相同的外部访问。
  • @jmcilhinney 我知道我可以在直接使用DirectCast 或创建该类型的变量时访问该属性,但这不是我的问题。我想知道为什么我可以从动态对象访问它,但它是公开的,而不是当它是朋友/内部的。关于 C# dynamic 关键字,AFAIK,与在 VB.NET 中使用 Option Strict Off 的对象相同。
  • “大概,VB 中的后期绑定仅适用于公共成员” 也许这就是原因,尽管后期绑定有时在同一个程序集中很有用。无论如何,C# 示例在所有编译器中的行为并不相同。

标签: c# .net vb.net derived-class access-levels


【解决方案1】:

我无法回答这个问题的原因,但我可以提供一个解决方法。

创建一个Interface 并在您的BaseClass 中实现它。然后,您可以将其声明为接口,而不是将Casted 声明为Object,这将正确地公开您想要的属性,并使用与以前相同的访问修饰符。

下面的完整示例。

Sub Foo()
    Dim targetClass As BaseClass = BaseClass.CreateDerived()

    Dim Casted As Interf
    If TypeOf (targetClass) Is DerivedClass1 Then
        Casted = DirectCast(targetClass, DerivedClass1)
    ElseIf TypeOf (targetClass) Is DerivedClass2 Then
        Casted = DirectCast(targetClass, DerivedClass2)
    Else
        Exit Sub
    End If

    Console.WriteLine(Casted.MyProperty) 'Throws an exception.
End Sub

Friend Interface Interf
    Property MyProperty As Integer
End Interface

Public Class BaseClass
    Implements Interf

    Private Shared Rand As New Random

    Friend Overridable Property MyProperty As Integer Implements Interf.MyProperty

    Public Shared Function CreateDerived() As BaseClass
        Return If(Rand.Next(1, 3) = 1, New DerivedClass1(), New DerivedClass2())
    End Function
End Class

Public Class DerivedClass1
    Inherits BaseClass

    Sub New()
        MyProperty = 1
    End Sub

    Friend Overrides Property MyProperty As Integer
End Class

Public Class DerivedClass2
    Inherits BaseClass

    Sub New()
        MyProperty = 2
    End Sub

    Friend Overrides Property MyProperty As Integer
End Class

【讨论】:

  • 感谢您的建议。
猜你喜欢
  • 2023-03-24
  • 1970-01-01
  • 2012-05-28
  • 1970-01-01
  • 1970-01-01
  • 2021-11-01
  • 1970-01-01
  • 2011-08-10
  • 1970-01-01
相关资源
最近更新 更多