我覺得我需要給出一些背景信息,說明爲什麼我在做我正在做的事情,因爲我想打開它的建議和批評,並給予希望使用Blender的XNA程序員可能會閱讀此內容。考慮到這篇文章的篇幅,如果你不關心我的問題的細節,請隨意跳到最後一段。通過動態對象的TryInvokeMember覆蓋使用擴展方法
我正在研究讀取.blend文件(由Blender創建)的XNA內容管道擴展項目,並將它們轉換爲可從XNA遊戲加載的數據,以避免必須導出新的.FBX或.OBJ模型。攪拌器每次我做任何小小的調整,以及(希望)爲Blender的真棒粒子和物理能力做一些XNA兼容的支持。
沒有深入Blender's inner workings,我想描述一下我對.blend文件如何工作的理解。如果你對這個主題有更多的瞭解,請糾正我。
攪拌器將文件保存在字節的「塊」中。這些塊中的大部分都包含表示3D場景中的對象和設置的數據,並且文件中的最後一個塊(稱爲SDNA塊)包含可以認爲是非常簡單的C型結構的東西,每個結構都有唯一的標識符,以及各種類型的幾個領域。這些結構的字段可以是簡單的類型,例如int
或float
,或者它們可以是在SDNA塊中定義的類型。
例如,這裏的ID
SDNA結構的半僞碼錶示:
structure IDPropertyData
{
void *pointer;
ListBase group;
int val;
int val2;
}
正如你可以看到,字段*pointer
,val
,和val2
可以在通過簡單的值運行時表示型號爲void*
或int
。然而,group
字段的類型爲ListBase
,它在文件的SDNA塊中的其他位置定義。我創建了一個類(稱爲BlenderObject
),它給出了一個SDNA結構(它是「SDNA類型」)和一個字節塊,它表現爲一個實例該結構通過爲其自身存儲簡單類型的值或存儲其他實例的集合,每個實例都代表其中一個「字段」。這使我的媒體庫的用戶編寫類似下面的代碼:
//Get a BlendContent instance that contains the file's information.
BlendContent content = BlendContent.Read(filePath);
//Get the 0th (and only) block containing data for the scene (code "SC")
BlendFileBlock sceneBlock = content.FileBlocks["SC", 0];
//Get the BlenderObject that represents the scene
dynamic scene = sceneBlock.Object;
//Get the scene's "r" field, whose SDNA type is RenderData.
dynamic renderData = scene.r;
//Get the x and y resolution of the rendered scene
float
xParts = renderData.xparts,
yParts = renderData.yparts;
scene
和renderData
都是「複雜」 BlenderObject
實例(每個字段的集合,而不是直接值),xparts
,和yparts
都是「簡單的」BlenderObject
實例(每個實例都有自己的直接簡單類型值,而不是字段集合)。如果其SDNA類型是程序集中的具體編譯類型,則每個BlenderObject
的行爲與您期望的完全相同,這是我使用動態來表示Blender對象的目標。
爲了簡化我的庫的使用,我正在努力使DynamicObject
的方法超載,使「簡單」BlenderObject
實例的行爲與其直接值相同。例如,讓foo
爲BlenderObject
,其中int
的值類型爲,例如4。我希望能夠做foo
如下:
string s = foo.ToString();
Console.WriteLine(s);
第一行的目的是調用foo
的ToString方法的內在價值,而不是foo
本身,所以foo
的TryInvokeMember
覆蓋使用反射來調用Int32.ToString()
的值爲4,而不是自己的BlenderObject.ToString()
。這種反射方法通話的偉大工程(也是如此應用於索引的數組類型的直接值相同的概念),除了當我嘗試類似如下:
string s = foo.Bar();
Console.WriteLine(s);
Bar
是我組裝定義的擴展方法,因此反映foo
的值爲4顯然未能找到它,當然,拋出異常。我的問題最後是:
如何查找和/或緩存可應用於對象的擴展方法?我知道how to find extension methods with reflection,但是每次在這個動態的BlenderObject
實例上調用擴展方法時都會這樣真的是慢。除了那個問題中描述的方法之外,是否有更快的方法來找到擴展方法?如果不是,我應該如何去實施擴展方法,所以我只需要找到一次,並可以快速訪問它們呢?對於長壽抱歉,並提前感謝任何有用的答案/評論。
編輯:
由於@spender回答,一個簡單的方法來解決我的問題是一個字典(雖然我使用Dictionary<Type, Dictionary<CallInfo, MethodInfo>>
輕鬆使用由InvokeMemberBinder
傳遞給DynamicObject.TryInvokeMember
提供的CallInfo)。但是我的實現給我帶來了另一個問題:
如何獲取調用程序集引用的程序集中定義的類型?例如,請考慮以下代碼: object Foo(dynamic blenderObject) return blenderObject.x.Baz(); } 如果此代碼在引用我的Blender庫的項目中,並且擴展方法Baz()
在該項目引用的另一個程序集中定義,但不是由我的攪拌器管道定義,那麼我將如何去從我的Blender管道中查找Baz()
?這甚至有可能嗎?