簡體   English   中英

如何使用反射來調用私有方法?

[英]How do I use reflection to invoke a private method?

我的類中有一組私有方法,我需要根據輸入值動態調用一個。 調用代碼和目標方法都在同一個實例中。 代碼如下所示:

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType);
dynMethod.Invoke(this, new object[] { methodParams });

在這種情況下, GetMethod()不會返回私有方法。 我需要向GetMethod()提供哪些BindingFlags以便它可以定位私有方法?

只需更改您的代碼以使用接受 BindingFlags GetMethod重載版本

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, 
    BindingFlags.NonPublic | BindingFlags.Instance);
dynMethod.Invoke(this, new object[] { methodParams });

這是BindingFlags 枚舉文檔

BindingFlags.NonPublic本身不會返回任何結果。 事實證明,將它與BindingFlags.Instance結合起來就行了。

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, 
    BindingFlags.NonPublic | BindingFlags.Instance);

如果你真的想讓自己陷入困境,可以通過編寫擴展方法使其更容易執行:

static class AccessExtensions
{
    public static object call(this object o, string methodName, params object[] args)
    {
        var mi = o.GetType ().GetMethod (methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance );
        if (mi != null) {
            return mi.Invoke (o, args);
        }
        return null;
    }
}

和用法:

    class Counter
    {
        public int count { get; private set; }
        void incr(int value) { count += value; }
    }

    [Test]
    public void making_questionable_life_choices()
    {
        Counter c = new Counter ();
        c.call ("incr", 2);             // "incr" is private !
        c.call ("incr", 3);
        Assert.AreEqual (5, c.count);
    }

Microsoft 最近修改了反射 API,使這些答案中的大部分都過時了。 以下應該適用於現代平台(包括 Xamarin.Forms 和 UWP):

obj.GetType().GetTypeInfo().GetDeclaredMethod("MethodName").Invoke(obj, yourArgsHere);

或者作為擴展方法:

public static object InvokeMethod<T>(this T obj, string methodName, params object[] args)
{
    var type = typeof(T);
    var method = type.GetTypeInfo().GetDeclaredMethod(methodName);
    return method.Invoke(obj, args);
}

筆記:

  • 如果所需的方法在obj的超類中,則必須將T泛型顯式設置為超類的類型。

  • 如果該方法是異步的,您可以使用await (Task) obj.InvokeMethod(…)

你確定這不能通過繼承來完成嗎? 反射是解決問題時最不應該考慮的事情,它使重構、理解代碼和任何自動分析變得更加困難。

看起來您應該只有一個 DrawItem1、DrawItem2 等覆蓋您的 dynMethod 的類。

特別是對私人成員的反思是錯誤的

  • 反射破壞了類型安全。 您可以嘗試調用一個不存在的方法(不再存在),或者使用錯誤的參數,或者使用過多的參數,或者不夠......甚至以錯誤的順序(這是我最喜歡的 :) )。 順便說一下,返回類型也可能會改變。
  • 反思很慢。

私有成員反射破壞了封裝原則,從而將您的代碼暴露給以下內容:

  • 增加代碼的復雜性,因為它必須處理類的內部行為。 隱藏的東西應該保持隱藏。
  • 使您的代碼易於破壞,因為它會編譯但如果方法更改其名稱則不會運行。
  • 使私有代碼易於破解,因為如果它是私有的,則不打算以這種方式調用。 也許私有方法在被調用之前需要一些內部狀態。

如果無論如何我必須這樣做怎么辦?

有這樣的情況,當你依賴第三方或者你需要一些沒有暴露的api時,你必須做一些反思。 有些人還使用它來測試他們擁有的一些類,但他們不想更改接口以僅用於測試來訪問內部成員。

如果你這樣做,就做對

  • 減輕易斷點:

為了緩解容易中斷的問題,最好的方法是通過在持續集成構建等中運行的單元測試中進行測試來檢測任何潛在的中斷。 當然,這意味着您始終使用相同的程序集(其中包含私有成員)。 如果您使用動態加載和反射,您喜歡玩火,但您始終可以捕獲調用可能產生的異常。

  • 減輕反射的緩慢:

在 .Net Framework 的最新版本中,CreateDelegate 以 50 倍的因子擊敗 MethodInfo 調用:

// The following should be done once since this does some reflection
var method = this.GetType().GetMethod("Draw_" + itemType, 
  BindingFlags.NonPublic | BindingFlags.Instance);

// Here we create a Func that targets the instance of type which has the 
// Draw_ItemType method
var draw = (Func<TInput, Output[]>)_method.CreateDelegate(
                 typeof(Func<TInput, TOutput[]>), this);

draw調用將比MethodInfo.Invoke快 50 倍左右,使用draw作為標准Func ,如下所示:

var res = draw(methodParams);

檢查我的這篇文章以查看不同方法調用的基准

難道您不能為要繪制的每種類型使用不同的 Draw 方法嗎? 然后調用重載的 Draw 方法,傳入要繪制的 itemType 類型的對象。

您的問題沒有說明 itemType 是否真正指的是不同類型的對象。

調用任何方法,盡管它在對象實例上具有保護級別。 享受!

public static object InvokeMethod(object obj, string methodName, params object[] methodParams)
{
    var methodParamTypes = methodParams?.Select(p => p.GetType()).ToArray() ?? new Type[] { };
    var bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
    MethodInfo method = null;
    var type = obj.GetType();
    while (method == null && type != null)
    {
        method = type.GetMethod(methodName, bindingFlags, Type.DefaultBinder, methodParamTypes, null);
        type = type.BaseType;
    }

    return method?.Invoke(obj, methodParams);
}

我認為您可以將它傳遞給BindingFlags.NonPublic ,其中GetMethod方法。

閱讀這個(補充)答案(有時就是答案)以了解這是怎么回事以及為什么這個線程中的一些人抱怨“它仍然不起作用”

我寫的代碼與這里的答案之一完全相同 但我仍然有一個問題。 我把斷點放在

var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance );

它執行了但是mi == null

它繼續這樣的行為,直到我對所有涉及的項目進行“重新構建”。 我正在對一個程序集進行單元測試,而反射方法則在第三個程序集中。 這完全令人困惑,但我使用 Immediate Window 來發現方法,我發現我嘗試進行單元測試的私有方法具有舊名稱(我重命名了它)。 這告訴我,即使單元測試項目構建,舊的程序集或 PDB 仍然存在 - 由於某種原因,它測試的項目沒有構建。 “重建”工作

應該注意的是,從派生類調用可能會有問題。

容易出錯:

this.GetType().GetMethod("PrivateTestMethod", BindingFlags.Instance | BindingFlags.NonPublic)

正確的:

typeof(CurrentClass).GetMethod("PrivateTestMethod", BindingFlags.Instance | BindingFlags.NonPublic)

BindingFlags.NonPublic

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM