返回首页


在过去的几年里中,编程语言和开发工具演变颇有几分。 Visual Studio是一个杰作如今,像)的东西。如果可能的话,根本没有引入的性能开销,或至少尽量减少它当是多目标的必要吗?
{S0}
这显然是必要的,如果您的应用程序,就是要针对不同的平台分布式(XBOX例如,PS3,PC等)。但您的软件多目标也是非常可取的,在其他情况下,例如当您需要升级您的技术,以一个较新版本的API。
例如,想象,DirectX11刚出来的时候。您想采取一些新的特点优势,但不能强迫所有客户升级他们的显卡,所以你仍然需要支持的DirectX10。解决的办法是使您的软件支持DirectX10与11多目标,。在这篇文章中,我们将使用针对两个不同的渲染API的应用程序的情况下:SlimDX和XNA
如果你已经面临这个问题之前,你知道它可以是一个严重的问题,特别是如果你的软件不被设计支持多目标从一开始,你有API调用散落在你的代码。我们如何做,而无需从头重新编写整个软件的变化?
让我们的探索的可能性:一个宏观的彗星的方法
使用C语言时,有些人使多目标使用宏:写所有你的游戏的逻辑代码使用自己的宏名为喜欢的类型:myMatrix4x4,myTexture等写一个头文件,其中每个平台的所有宏都宣布:

#define myVector3 Microsoft::Xna::Framework::Vector3

#define myMatrix4x4 Microsoft::Xna::Framework::Matrix

但是要记住,宏是万恶之源在世人面前。
宏很容易在复杂性的增加。特别是如果你开始做多层次的宏调用。它们使调试和理解软件人间地狱,是极其错误倾向的,如果你不小心,。他们甚至从现代语言如C#。所以我完全打消您使用此solution.Pros:一切都安排在编译每个平台:没有性能损失。没有设计工作。易于开发解决:在编译时没有性能开销的缺点:使调试,跟踪和维护的软件,一般在地球的地狱非常容易发生错误不现代语言,如C#在我看来,对软件中的良好做法的。至少只要存在一个更好的解决方案。 C项目大量使用宏汇编,需要几年时间才能完成,特别是如果宏是递归的,他们不为每一种情况下的工作条件编译的方法
正如我们已经看到,你的代码的某些部分必须是不同的每个平台的,所以是一个很明显的,简便的方法,使您的软件多目标使用条件编译
这种方法几乎每一种语言的支持,并通过在您的项目中定义编译常量像:PLATFORM_XNA,或PLATFORM_SLIMDX。然后,每次你找到一个API相关的代码部分,你是这样的:
#if(PLATFORM_XNA)

...

#else

...

#endif
优点:一切都安排在编译每个平台:没有性能损失。没有设计工作。易于开发所有语言的缺点:丑和联合国优雅的代码,了解和跟踪您的代码将大幅增长(冗员过多)在我看来,它反对在软件中的良好做法的不舒服。至少只要存在一个更好的解决方案。分层的方法
这是通常的方法之一。它关系到软件工程超过一个特定的语言,涉及到所有的发展阶段,构想和一流的设计,因为,最终的编码。
它表明什么是您的应用程序除以在"图层???保持游戏逻辑相关的问题,它不依赖于所有平台API或内部层和外部层,这将使所有的逻辑输出,通过API,。这样,当您需要针对不同的平台时,只有在外部层已被改写
这外部层,通常像渲染,这是为什么它是非常典型的以找到游戏,与DLL中有像优惠:。RendererDX9 DLL,RendererDX10.dll,etc.Pros:优雅易于理解,跟踪和调试乐百氏,不是太容易出现错误,在所有语言的缺点:它需要一个大的设计工作,它可以引入一个位性能开销,它引入了冗余代码和信息,存储在几层,有时不是一个可行的解决办法,如果该项目已写入(多目标不是从一开始就计划),因为这意味着巨大的结构性变化,如一些游戏状态。这种设计会给你的软件库或API,我个人不喜欢,因为它打破了分离任务类的一致性,即概念应该属于一类到其他组件。这是我个人不同意这种做法的要点之一。我宁愿保持这种一致性,使涉及到它的类内的对象所有任务。只是一个例子来说明:
您将结束行代码,如:
RendererDX9.RenderModel(this);

代替了传统的:
this.Render();

有些人会说,渲染的对象不是自然属于该对象的任务。好了,正如我所说,这是一个个人喜好的问题,,我更喜欢第二种方法。这就是为什么在下一章,我们将努力使遵循它。继承方式
这种方法是优雅以及与前一个多目标系统。它也涉及到工程设计和规划的一个特定的语言,也涉及许多发展阶段。
,而不是在层层分您的软件,它主要依赖继承,通过一个类API的受养人如在下一个例子很基本的设计:Model3DBase Model3DSlimDX Model3DXNA
这是很明显,我们会在基类的所有非API相关的代码,并在其余的子类。例如,ToString()方法将呈现三维模型,所以该代码将在基类中的平台无关,避免重写它为每个孩子。
一旦我们拥有的所有类分为每个版本的API,在您的软件外,你只需要选择使用哪一种场景的。是这样的:SceneSlimDX或SceneXNA。
这种方法的作品,典雅,轻松,以了解和跟踪,并保持类的一致性,但它有一个主要缺点:这样的类(一个三维模型),许多许多成员变量和方法会被空气污染指数,特定的,因此在基类中的代码量将远远高于孩子,依赖类低。这将迫使你写重复的代码的数量巨大,我们已经说过,重复是错误的。优点:优雅易于理解,跟踪和调试乐百氏,没有太多的错误倾向的保持类的一致性,让我们把里面的Model3D类的Render方法,而不必把它拿出另一个类,在所有语言。缺点:它需要一些设计工作,有时不是一个可行的解决办法,如果该项目已经写入(多目标不是从一开始就计划),因为这意味着巨大的结构性变化。分割你的类型,为每个API不会有助于减少较高的代码级别的冗余(我们将使用该模型时会发生什么?哪一个版本将使用?同样条件编译?)最大的问题是提到的代码冗余(许多部分代码将在子类中重复),所以,哪一个是最好的呢?
再次,这是一个个人喜好的问题。
宏都将被丢弃自己??太容易发生错误,并在现代语言中不存在。在我看来,最好的选择是组合:一个有点条件编译(在极少数情况下),并在某些情况下的分层设计有点太。但是,无论我们选择,他们都有同样的问题:代码冗余。
如果我们能减少冗余的代码吗??/ P> C#来保存一天
。NET已引入改善软件,因为它的概念的发展。节省您的时间。它可以节省你的钱。它可以节省你头疼。这样可以节省你的压力。在我看来,这正是。NET的强大的卖点。微软已经成功地使程序员??的生活更轻松,没有放弃的性能,效率和健壮性的最新版本
,NET是进一步,引入新的发展途径,可以,不仅使您的生活更轻松,而且还为您提供的可能性,要面对的新途径老问题。这种情况下,只是一个example.Extension方法
很多时候,API是彼此相似。您将有纹理,你将有三维模型,您将有顶点和面。甚至呈现方法将可能相似。这是一个痛苦,只是想找到的三维模型网格顶点的东西添加条件编译:
#if(RENDER_XNA)

            int vertexCount = mMesh.NumVertices;

#elif(RENDER_SlimDX9)

            int vertexCount = mMesh.VertexCount;

#endif

新华社和SlimDX提供的信息,但物权变动的名称。有很多这样的情况下改变代码只是语义,或重新安排的东西。这是一个可惜这样一个简单的事情,在你的代码添加冗余。
当然,我们可以把一个"GetVertexCount??方法或"VertexCount??在我们的Model3D类的属性,但??会发生什么事,当我们的工作与简单的网格(没有我们自己的Model3D类型) ?由于扩展方法可以帮助条件编译呢?
尚未吗??在这里??/ P>扩展方法是什么?
,我们可以很容易地代码"GetVertexCount这样的方法吗??C5}
注意:你只需要添加该方法在您的DLL静态类,它拥有扩展方法
有了这个,新华社的ModelMeshPart SlimDX的网格将提供GetVertexCount方法。具有完全相同接口。所以,我们的代码的任何部分,希望有此信息将不需要再添加条件编译,也不冗余
,我们可以做很多差异之间的API类似的东西:物业更名或迁移,方法与不同的或重新安排参数,甚至创造一个API,而不是在其他存在的方法,。我们的目标是统一possible.What表现?
我们那里没有任何一个方法调用两个API接口。因此,从理论上讲,我们加入一个性能开销。但这里是Visual Studio编译器来帮助。
正如你已经知道,有一件事,所谓内联,将帮助这里。当你的代码被编译时,编译器将取代任何呼叫与它的真正的内码GetVertexCount方法。所以没有性能开销是真的。
然而,你应该采取内联照顾,因为有这几个条件发生:大于32字节的IL的方法不会被内联。虚函数内联。有复杂的流量控制的方法不会在成荫。复杂的流控制是比IF / THEN / ELSE其他任何流量控制,在这种情况下,开关或同时。包含的异常处理块的方法是不联,抛出异常的方法,但仍然是内联的候选人。如果任何方法的形式参数是结构,该方法将不被内联。
更多信息内联:{A2},并在我们的项目信息和代码冗余。减少重复甚至更多,仿制药
除了使用扩展方法,正如我们在前面的章节中看到的,我们可以减少条件编译和代码冗余,甚至更多,使用泛型。
很多时候,我们的代码的某些部分需要添加条件编译,仅仅是因为使用的主要是每个API不同,但对他们所执行的操作不需要知道它们实际上是什么类型。举例来说,如果我们创建了一个材料类,它可能会举行纹理列表,纹理类型是API的依赖。我们需要添加条件编译每次我们要处理的纹理列表吗?也许不是
材料类是通用的,将使我们工作的纹理,不知道如果他们真的是SlimDX或XNA纹理:
public class Material<T_Texture>

{

     protected List<T_Texture> mTextures;

}

,可降低材料类的冗余,可以不知道类型的情况下做任何纹理的名单管理:添加或删除纹理,访问他们,etc.What如果我们仍然需要执行一个API在泛型类的相关操作
有时,你的类不会是纯粹的仿制,而且仍然需要执行一些API的具体操作。在这种情况下,可以随时查询,通用T_Texture的类型实际上是什么。
就像下面的例子中,泛型类型进行检查,以返回一个值类型,或其他:
public static T GetRenderState<T>(this Microsoft.DirectX.Direct3D.Device pSrc, RenderState pState)

{

    if (typeof(T) == typeof(int))

        return (T)Convert.ChangeType(pSrc.GetRenderStateInt32((RenderStates)pState), typeof(T));

    else if (typeof(T) == typeof(float))

        return (T)Convert.ChangeType(pSrc.GetRenderStateSingle((RenderStates)pState), typeof(T));

    else if (typeof(T) == typeof(bool))

        return (T)Convert.ChangeType(pSrc.GetRenderStateBoolean((RenderStates)pState), typeof(T));

    else throw new System.ApplicationException("Generic Type invalid");

}
使用泛型(材料)
类材料类完成时,我们可能会想在一个Model3D类中使用它。我们只是需要做一些事情,如:
public class Model3D

{

    #if(RENDER_XNA)

         protected Material<Microsoft.Xna.Framework.Graphics.Texture2D> mMaterial;

    #elif(RENDER_SlimDX9)

protected Material<SlimDX.Direct3D9.Texture> mMaterial;

    #endif

}

这种方式,我们会删除从材料类或任何条件编译代码冗余,并把它添加只是几次声明和实例mMaterial variable.Going有条件#using语句
更进一步这是很明显的,但无论如何,它可能会帮助别人。
有很多次,类型有几个API相同的名称。举例来说,存在的类型VertexBuffer:XNA,SlimDX和托管DirectX。
对于这种情况,我们可以减少条件编译和代码冗余甚至更多,用#在每个代码文件的开头使用的报表。假设我们有一个像下面的类号XXX:
public class XXX

{

    #if(RENDER_XNA)

        protected Microsoft.Xna.Framework.Graphics.VertexBuffer mVertexBuffer;

    #elif(RENDER_SlimDX9)

protected SlimDX.Direct3D9.VertexBuffer mVertexBuffer;

    #endif

}

在这种情况下,我们只是因为添加的命名空间的类型变化的条件编译。只有一次在#using语句,避免他们都不遗余力通过您的代码添加条件编译好得多:
#if(RENDER_XNA)

    #using Microsoft.Xna.Framework.Graphics;

#elif(RENDER_SlimDX9)

#using SlimDX.Direct3D9;

#endif



public class XXX

{

    protected VertexBuffer mVertexBuffer;

}
使用通用的约束,以减少甚至更多的裁员
我们仍然没有提及其他的可能性泛型约束一样,。约束是一种方式来告诉的环境,即使当一个类型是通用的(像T_Texture),它满足了一定的制约,比如有一个构造,或继承一个类或者接口。只是一个例子:
public class Model3DBase<T_Material, T_Texture>

    where T_Material : MaterialBase<T_Texture>

{

        protected List<T_Material> mMaterials;

}

这使得一个巨大的差异,减少重复像现在,即使不指定任何T_Material类型,我们将能够访问的所有属性和方法在MaterialBase指定的。
其他的例子:约束


,其中T:结构
类型参数必须是值类型。任何值类型,除了泛型{A3}将解释你这个问题。
结论的C#和。NET面临的老问题提供了新的途径。本文试图在一个非常古老的的问题如何采取一些新的功能如泛型和扩展方法的优点:API的独立3D引擎。
因此,我们采取了这些新功能,并结合面临这个问题的老办法,来一个新的,改进的实施,有一个基本的目标:减少代码冗余
希望你喜欢它{BR。 }干杯!

回答

评论会员:JH64 时间:2012/01/27
我学到了很多,从这篇文章
评论会员:。kornman00 时间:2012/01/27
什么?没有的typedef {S1} {BR爱吗?}
我错过的能力,尤其是在这样的情况下提供,模板。特别是在GetRenderState的情况下,类型专业化已使用(但这里结束自己的值类型,潜在的联合国/拳击,转换,增加额外的开销时,他们已经知道了!*叹息*,我想念模板)。