Dot-Net

.NET 4.5 CustomReflectionContext:它有什麼用?

  • March 25, 2016

.NET Framework 4.5 Developer Preview 中的新增功能提到

能夠自定義反射上下文以通過CustomReflectionContext類覆蓋預設反射行為。

的目的是ReflectionContext什麼?MSDN 對這個主題不太清楚。

在過去的 .NET 中,希望能夠通過反射自動化某些功能與能夠自定義它們之間存在矛盾。例如,以 Visual Studio 中的“屬性”面板為例——在顯示某些 .NET 類型(例如,設計圖面上的控制項)的場景中,它可以自動發現並顯示該類型定義的每個公共屬性。

對這種類型驅動的行為使用反射很有用,因為這意味著每個屬性都會顯示出來,而控制項的開發人員不需要做任何事情。但它提出了一個問題:如果您想自定義事物,例如定義分類或為特定屬性自定義編輯 UI,該怎麼辦?

.NET 中的經典解決方案是將一堆自定義屬性添加到相關成員上。但是,這樣做的一個問題是,它可能意味著在執行時執行有意義的工作的程式碼部分最終取決於僅在設計時執行任何操作的類 - 依賴屬性會阻止您分離執行時和設計時方面. 您是否真的想將 VS 屬性面板的自定義設計器 UI 的程式碼作為最終在最終使用者電腦上的控制項庫的一部分提供?

另一個問題是,在某些情況下,您可能希望動態決定您呈現的“屬性”。最古老的範例之一(可追溯到 .NET 1.0)是將 aDataSet放入某種網格控制項(客戶端或 Web)中。對於強類型數據集,反射可能是網格發現源提供哪些屬性的合適方法,但DataSet也可以動態使用,因此您需要一種方法讓數據網格在執行時詢問要顯示哪些列。

(對此的一個答案是:正確設計您的 UI!像這樣直接生成網格會導致糟糕的使用者體驗。但是,很多人都想以懶惰的方式來做,無論這是否是個好主意……)

因此,您會遇到一種情況,有時您想要反射驅動的行為,但有時您希望能夠在執行時完全控制。

為此出現了各種臨時解決方案。您擁有完整TypeDescriptorPropertyDescriptor類型家族,它們在反射之上提供了一種虛擬化視圖。預設情況下,這將直接從反射傳遞所有內容,但類型有機會選擇在執行時提供自定義描述符,使它們能夠修改甚至完全替換它們的外觀。ICustomTypeDescriptor是那個世界的一部分。

這為預設情況下需要反射驅動行為的問題提供了一種解決方案,如果需要,可以選擇提供執行時驅動的行為。但它並不能解決您只想在設計時執行此操作的問題,並且您不想將該程式碼作為執行時可再發行文件的一部分發布。

所以幾年前,Visual Studio 引入了自己的特殊機制,用於在設計時增加類型資訊。有一堆約定驅動的行為,其中 Visual Studio 將自動發現與特定執行時組件相關的設計時組件,使您能夠自定義設計體驗,而無需將相關程式碼烘焙到您的可再發行組件中。Blend 也使用這種機制,儘管進行了一些調整,從而可以為 VS 和 Blend 提供不同的設計師作品。

當然,通過普通的反射 API 看不到這些——VS 和 Blend 有一個位於反射之上的包裝層,以使這一切正常工作。

所以現在我們有兩個虛擬化層可以通過反射,或者可以增強反射產生的東西……

看起來在 .NET 4.5 中,CLR 團隊決定,因為各個小組已經在做這種事情,而其他小組想要做更多的事情(MEF 團隊對反射驅動和可選執行時有類似的要求-增強行為),這正是應該內置到執行時中的那種東西。

新模型似乎是這樣的:ReflectionContext基類是一個抽象 API,通過它您可以獲得反射 API 的虛擬化版本。它看似簡單,因為其中一個主要思想是,如果您的唯一目標是在反射之​​上獲得一個可虛擬化的包裝器,那麼您不再需要像類型描述符系統這樣的專用 API——反射現在是開箱即用的虛擬化。所以你可以寫這種東西

public static void ShowAllAttributes(Type t)
{
   foreach (Attribute attr in t.GetCustomAttributes(true))
   {
       Console.WriteLine(attr);
   }
}

現在您總是能夠編寫它,但在 .NET 4.5 之前,這樣的程式碼將始終針對“真實”類型資訊工作,因為它使用反射。但是由於反射上下文,現在可以為它提供一個虛擬化的Type. 所以考慮這個非常無聊的類型:

class NoRealAttributes
{
}

如果你只是傳遞typeof(NoRealAttributes)給我的ShowAllAttributes方法,它什麼也不會列印出來。但我可以編寫一個(有點做作的)自定義反射上下文:

class MyReflectionContext : CustomReflectionContext
{
   protected override IEnumerable<object> GetCustomAttributes(MemberInfo member, IEnumerable<object> declaredAttributes)
   {
       if (member == typeof(NoRealAttributes))
       {
           return new[] { new DefaultMemberAttribute("Foo") };
       }
       else
       {
           return base.GetCustomAttributes(member, declaredAttributes);
       }
   }
}

(順便說一下,我認為CustomReflectionContext它和它的基礎之間的區別在於ReflectionContext後者定義了可虛擬化反射上下文的 API,同時CustomReflectionContext添加了一些幫助程序以使您更容易實現這樣的事情。)現在我可以使用它了Type為我的班級提供虛擬化版本:

var ctx = new MyReflectionContext();
Type mapped = ctx.MapType(typeof(NoRealAttributes).GetTypeInfo());
ShowAllAttributes(mapped);

在這段程式碼中,mapped仍然引用一個Type對象,所以任何知道如何使用反射 API 的東西都可以使用它,但它現在會報告一個實際上並不存在的屬性的存在。當然,Type是抽象的,所以我們總是有一些從中派生出來的東西,如果你打電話mapped.GetType(),你會發現它實際上是 aSystem.Reflection.Context.Custom.CustomType而不是System.RuntimeType你通常看到的。並且該CustomType對象屬於我的自定義上下文,因此您通過它獲得的任何其他反射 API 對象(例如,如果您編寫mapped.Assembly.GetTypes())您還將獲得通過我的自定義上下文的自定義對象,這將有機會修改任何內容否則出來的。

因此程式碼可以使用自定義對像在類型系統中導航Type。即使這樣的程式碼使用的是普通的基本反射 API,如果我認為合適的話,我現在有機會自定義任何由此產生的東西。

如果您要求,您只會獲得此虛擬化視圖。例如,.NET 4.5 中的 MEF 會查找一個自定義屬性,指定它應該使用使用者提供的自定義反射上下文,否則將回退到普通反射。(就我的ShowAllAttributes方法而言,它使用Type我選擇傳入的任何對象——它不知道它獲得的是虛擬對像還是“真實”類型的對象。)

所以簡而言之,這意味著如果您想要虛擬化類型資訊,您不再需要圍繞反射 API 的臨時包裝器。

引用自:https://stackoverflow.com/questions/9507270