【问题标题】:Can't reflect on Private Methods无法反映私有方法
【发布时间】:2012-01-06 01:29:32
【问题描述】:

所以我创建了这个帖子:Invoking Private / Protected Methods Via Reflection From The Same Object Instance (or Base)

我们已经解决了除了私有方法之外的问题。由于这可能不是同一个问题,我认为最好用完整的来源发布一个不同的问题。它仍在进行中,但可以正常工作。

基类:

Public MustInherit Class BaseTransactionalSaveManager : Implements ITransactionalSaveManager

    '---- Public Properties & Backing Fields ----'

    Public Property FormDataIsValid As Boolean Implements ITransactionalSaveManager.FormDataIsValid

    '---- Private Properties & Backing Fields ----'

    Protected Property Stages As Collections.Generic.List(Of String)
    Protected Property StageCausedRollback As Containers.GenericNamedValuePair(Of String, Boolean)
    Protected Property CurrentStage As Integer

    '---- Event Declarations & Associated Methods ----'

    Public Event TransactionCancelled As EventHandler(Of CustomEventArgs.GenericSingleEventArgs(Of String)) Implements ITransactionalSaveManager.TransactionCancelled

    Public Event TransactionCompleted As EventHandler(Of CustomEventArgs.GenericSingleEventArgs(Of String)) Implements ITransactionalSaveManager.TransactionCompleted

    Public Event TransactionStagePassed As EventHandler(Of CustomEventArgs.GenericSingleEventArgs(Of String)) Implements ITransactionalSaveManager.TransactionStagePassed

    Protected Overridable Sub OnTransactionCancelled(e As CustomEventArgs.GenericSingleEventArgs(Of String)) Implements ITransactionalSaveManager.OnTransactionCancelled

        RaiseEvent TransactionCancelled(Me, e)

    End Sub

    Protected Overridable Sub OnTransactionCompleted(e As CustomEventArgs.GenericSingleEventArgs(Of String)) Implements ITransactionalSaveManager.OnTransactionCompleted

        RaiseEvent TransactionCompleted(Me, e)

    End Sub

    Protected Overridable Sub OnTransactionStagePassed(e As CustomEventArgs.GenericSingleEventArgs(Of String)) Implements ITransactionalSaveManager.OnTransactionStagePassed

        RaiseEvent TransactionStagePassed(Me, e)

    End Sub

    '---- Constructors ----'

    Public Sub New()

        Stages = New Collections.Generic.List(Of String)
        SetStages()
        CurrentStage = 0

        StageCausedRollback = New Containers.GenericNamedValuePair(Of String, Boolean)
        FormDataIsValid = True

    End Sub

    '---- Public Methods ----'

    Public Sub ProcessStage() Implements ITransactionalSaveManager.ProcessStage

        ' Use stage to fire the correct method.

        Me.GetType.InvokeMember(Stages(CurrentStage),
                                Reflection.BindingFlags.InvokeMethod Or
                                Reflection.BindingFlags.NonPublic Or
                                Reflection.BindingFlags.Public Or
                                Reflection.BindingFlags.Instance,
                                Type.DefaultBinder, Me, Nothing)

        ' Determine if the stage should cause a rollback.

        If Not StageCausedRollback.Value Then

            RollBackTransaction(StageCausedRollback.Name)
            Exit Sub

        End If

        ' Check if this stage is the last one.

        If Stages(CurrentStage) = Stages.Last Then

            OnTransactionCompleted(New CustomEventArgs.GenericSingleEventArgs(Of String)(Stages(CurrentStage)))

        Else

            OnTransactionStagePassed(New CustomEventArgs.GenericSingleEventArgs(Of String)(Stages(CurrentStage)))

        End If

    End Sub

    Public Overridable Function TryCancelTransaction() As Boolean Implements ITransactionalSaveManager.TryCancelTransaction

        OnTransactionCancelled(New CustomEventArgs.GenericSingleEventArgs(Of String)(""))
        Return True

    End Function

    '--- Protected & Overridable Methods ----'

    Protected Overridable Sub SetStages()

        Me.Stages.Add(MethodNameToString(AddressOf Me.ConfirmFormDataIsValid))

    End Sub

    Protected Overridable Sub RollBackTransaction(stageThatCauseRollback As String)

        OnTransactionCancelled(New CustomEventArgs.GenericSingleEventArgs(Of String)(stageThatCauseRollback))

    End Sub

    Protected Function MethodNameToString(addressOfMethod As Action) As String

        Return addressOfMethod.Method.Name

    End Function

    Private Sub ConfirmFormDataIsValid()

        StageCausedRollback.Name = MethodNameToString(AddressOf ConfirmFormDataIsValid)
        StageCausedRollback.Value = If(FormDataIsValid, True, False)

    End Sub

End Class

所以这个类被一个(到目前为止)空的子类继承,并且 ProcessStage 被调用。请注意 ConfirmFormDataIsValid() sub 是私有的。如果您运行它,它将找不到此方法。如果我将其更改为受保护但它工作正常。

我错过了什么吗?

【问题讨论】:

  • 感谢 @vcsjones 修复突出显示...我做错了什么?
  • 回复:“心理上的语法突出显示”。语法荧光笔使用标签来决定代码是用什么语言编写的。因为你有 C# 标签;它在 VB.NET 上选择 C# 突出显示。您还可以使用an HTML comment 明确说明代码的语言。
  • 您是否考虑过Template Method Pattern 而不是这里的反射?我敢打赌它会更稳定。 (此评论基于此链接问题。)
  • Re: C# over VB.Net.... 当然不总是这样 :) 我把 C# 卡在那里,因为我可以读写两者,所以我不介意人们回答无论他们喜欢什么语言。 @ Austin Salonen - 实际上这就是我所得到的,唯一的问题是需要调用的内容可以按任何顺序进行,因此一旦用户抽象出来,他们就可以按不同的顺序将 20 种方法粘贴在那里。我想让他们列出他们希望调用它们的方法和顺序可能是最简单的,我会调用它们。
  • 我似乎找不到有用的例子,但this 文章回答了你的问题,我想,在某处......这似乎是一个信任/权限问题。

标签: vb.net reflection


【解决方案1】:

您需要调用Me.GetType.BaseType.InvokeMember,而不是Me.GetType.InvokeMember(ProcessStage 的第一行)

您不会在子类中看到私有成员,即使是 BindingFlags.NonPublic

显然,该解决方案会有点脆弱,因为它取决于您有多少级别的子类,您是否会在 BaseType 中看到该方法。您可能需要循环类链,直到达到 BaseTransactionalSaveManager 的 BaseType,然后找到方法。

希望对您有所帮助。

【讨论】:

  • 我们在同一页上,上面列出的代码是基类,这仍然适用吗?
  • 是的,因为“Me”是指由调用者创建的类型,而不是代码实际所在的基类。如果您从内部的方法调用 Me.GetType().Name基类,您会看到它始终是基类的名称。当然,如果你真的创建了一个基类类型的实例,情况就不一样了,但是你把它设为了 MustInherit,所以这是不可能的。
  • 啊,是的。再次阅读时,我意识到了这一点。嗯......我可能需要重新设计......无论哪种方式,这都能正确解决问题并提供原因......我的朋友为你打勾!
【解决方案2】:

这确实不能回答您的问题,但希望能给您一些使用模板方法模式的灵感。

首先,您通过方法创建这些方法名称的列表违反了 DRY。现在,如果您对方法进行重命名,则必须在 2 个地方进行更改。

通过使用模板方法模式,您提供了一个抽象方法,其中子类以正确的顺序定义了他们想要调用的所有方法(替换他们现在提供的列表)。您失去了反射到这些方法的所有开销,并且开发人员不再局限于没有方法参数。

此外,如果有一种无需反射的好方法,它通常是一种更简洁、更易于理解的解决方案。

-- CW,因为它确实是一个很长的评论,并没有回答手头的问题。

【讨论】:

  • +1 我完全同意。我不喜欢这样你的建议。 (PS 重构 -> 重命名意味着我只需更改一次 :))
猜你喜欢
  • 1970-01-01
  • 2023-03-03
  • 2022-11-27
  • 2012-04-02
  • 2019-01-26
  • 1970-01-01
  • 1970-01-01
  • 2012-03-06
  • 2023-03-18
相关资源
最近更新 更多