詳解 .NET 反射中的 BindingFlags 以及常用的 BindingFlags 使用方式

  • 2020 年 2 月 10 日
  • 筆記

使用 .NET 的反射 API 時,通常會要求我們傳入一個 BindingFlags 參數用於指定反射查找的範圍。不過如果對反射不熟的話,第一次寫反射很容易寫錯導致找不到需要的類型成員。

本文介紹 BindingFlags 中的各個枚舉標記的含義、用途,以及常用的組合使用方式。


所有的 BindingFlags

默認值

// 默認值  Default

查找

這些標記用於反射的時候查找類型成員:

// 表示查找的時候,需要忽略大小寫。  IgnoreCase    // 僅查找此特定類型中聲明的成員,而不會包括這個類繼承得到的成員。  DeclaredOnly    // 僅查找類型中的實例成員。  Instance    // 僅查找類型中的靜態成員。  Static    // 僅查找類型中的公共成員。  Public    // 僅查找類型中的非公共成員(internal protected private)  NonPublic    // 會查找此特定類型繼承樹上得到的靜態成員。但僅繼承公共(public)靜態成員和受保護(protected)靜態成員;不包含私有靜態成員,也不包含嵌套類型。  FlattenHierarchy

調用

這些標記用於為 InvokeMember 方法提供參數,告知應該如何反射調用一個方法:

// 調用方法。  InvokeMethod    // 創建實例。  CreateInstance    // 獲取欄位的值。  GetField    // 設置欄位的值。  SetField    // 獲取屬性的值。  GetProperty    // 設置屬性的值。  SetProperty

其他

接下來下面的部分就不是那麼常用的了。

這些標記用於為 InvokeMember 方法提供參數,但是僅在調用一個 COM 組件的時候才應該使用:

PutDispProperty  PutRefDispProperty
ExactBinding  SuppressChangeType
OptionalParamBinding

下面是一些雜項……

// 忽略返回值(在 COM 組件的互操作中使用)  IgnoreReturn    // 反射調用方法時如果出現了異常,通常反射會用 TargetInvocationException 包裝這個異常。  // 此標記用于禁止把異常包裝到 TargetInvocationException 中。  DoNotWrapExceptions

你可能會有的疑問

  1. 如果 A 程式集對 B 程式集內部可見(InternalsVisibleTo("B")),那麼 B 在反射查找 A 的時候,internal 成員的查找應該使用 Public 還是 NonPublic 標記呢?
    • 依然是 NonPublic 標記。
    • 因為反射的是程式集的元數據,這是靜態的數據,跟運行時狀態是無關的。

常用的組合

從上面的解釋中可以發現,這個類型的設計其實是有問題的,不符合單一職責原則。所以我們會在不同的使用場景下使用不同區域的組合。

查找,也就是獲取一個類型中的欄位、屬性、方法等的時候使用的。

拿到所有成員:

BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance

實際上 RuntimeReflectionExtensions.Everything 屬性就是這麼寫的。

拿到公有的實例成員:

BindingFlags.Public | BindingFlags.Instance

附 BindingFlags 的源碼

[Flags]  public enum BindingFlags  {      // NOTES: We have lookup masks defined in RuntimeType and Activator.  If we      //    change the lookup values then these masks may need to change also.        // a place holder for no flag specifed      Default = 0x00,        // These flags indicate what to search for when binding      IgnoreCase = 0x01,          // Ignore the case of Names while searching      DeclaredOnly = 0x02,        // Only look at the members declared on the Type      Instance = 0x04,            // Include Instance members in search      Static = 0x08,              // Include Static members in search      Public = 0x10,              // Include Public members in search      NonPublic = 0x20,           // Include Non-Public members in search      FlattenHierarchy = 0x40,    // Rollup the statics into the class.        // These flags are used by InvokeMember to determine      // what type of member we are trying to Invoke.      // BindingAccess = 0xFF00;      InvokeMethod = 0x0100,      CreateInstance = 0x0200,      GetField = 0x0400,      SetField = 0x0800,      GetProperty = 0x1000,      SetProperty = 0x2000,        // These flags are also used by InvokeMember but they should only      // be used when calling InvokeMember on a COM object.      PutDispProperty = 0x4000,      PutRefDispProperty = 0x8000,        ExactBinding = 0x010000,    // Bind with Exact Type matching, No Change type      SuppressChangeType = 0x020000,        // DefaultValueBinding will return the set of methods having ArgCount or      //    more parameters.  This is used for default values, etc.      OptionalParamBinding = 0x040000,        // These are a couple of misc attributes used      IgnoreReturn = 0x01000000,  // This is used in COM Interop      DoNotWrapExceptions = 0x02000000, // Disables wrapping exceptions in TargetInvocationException  }

參考資料

本文會經常更新,請閱讀原文: https://blog.walterlv.com/post/binding-

本作品採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名 呂毅 (包含鏈接: https://blog.walterlv.com ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發布。如有任何疑問,請 與我聯繫 ([email protected])