我觉得我需要给出一些背景信息,说明为什么我在做我正在做的事情,因为我想打开它的建议和批评,并给予希望使用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()
?这甚至有可能吗?