跳过正文
  1. 项目/
  2. MonoGame 鸿蒙移植项目/

MonoGame 移植:C# partial 类机制与 PlatformSetup 覆写

·233 字·2 分钟·
FunBocchi
作者
FunBocchi
游戏引擎、图形编程爱好者。正在把 MonoGame 3.8.x 逐行移植为 C++20 并跑在鸿蒙上;也在做 Bocchi House 游戏移植平台与各种经典游戏的 HarmonyOS 移植。
目录
MonoGame 鸿蒙移植 - 这篇文章属于一个选集。
§ : 本文

为什么 partial 机制是理解 MonoGame 的关键
#

MonoGame 大量使用 C# 的 partial 类做平台适配。GraphicsDevice 在源码里是这样的:

// Graphics/GraphicsDevice.cs(前端)
public partial class GraphicsDevice : IDisposable { ... }

// Platform/Graphics/GraphicsDevice.DirectX.cs(DX 平台)
public partial class GraphicsDevice { ... }

多个源文件声明同一个类,编译时合并为一个类。关键规则:partial 类的所有成员(含 private)对所有 partial 部分可见。所以前端文件里调用的 PlatformSetup(),只要任何一个 partial 文件定义了它,就能编译——方法归属是"整个类",不是"某个文件"。

PlatformSetup 覆写的真相
#

很多人第一次看会困惑:GraphicsDevice.cs 里调用了 PlatformSetup(),但前端文件里找不到它的声明——它去哪了?

答案:这不是继承覆写(override),而是 partial 类跨文件补充定义

// 前端 Setup()(GraphicsDevice.cs L349):直接调用,无声明
private void Setup() { ...; PlatformSetup(); ... }

// 平台实现(GraphicsDevice.DirectX.cs L53):定义同名方法
private void PlatformSetup()
{
    MaxTextureSlots = 16;
    MaxVertexTextureSlots = 16;
    CreateDeviceResources();
}

partial 类编译后是同一个类,前端调用点与平台实现自动配对。

两类钩子的形态
#

MonoGame 源码里平台钩子有两种写法:

形态 A:partial 方法(声明在前端,实现可选)
#

// 前端(GraphicsDevice.cs L756):只声明,无实现
partial void PlatformReset();

// 平台(GraphicsDevice.DirectX.cs L73):实现
partial void PlatformReset() { CorrectBackBufferSize(); ... }

partial 方法的实现可以省略(省略后调用点被编译器移除),适合"可选钩子"。

形态 B:跨 partial 文件私有方法(前端直接调用,平台必须定义)
#

// 前端:直接调用 PlatformSetup();
// 平台:private void PlatformSetup() { ... }

平台必须提供实现,否则编译/链接失败。PlatformSetup/PlatformInitialize/PlatformClear/PlatformDraw* 等核心钩子都是这种。

平台实现对比:同一个 PlatformSetup,两个平台各写各的
#

步骤DirectXOpenGL
上下文/设备CreateDeviceResources()(D3D11Device)GL.CreateContext(windowInfo)
能力查询_d3dDevice.FeatureLevelGL.GetInteger(...)
槽位上限写死 16查询驱动实际值

前端只要求"调用后 MaxTextureSlots 等成员被填好",平台负责"怎么填"

C++ 移植对应
#

C# 的 partial 合并,在 C++ 里对应"基类 + 钩子":

// 基类(graphics_device.hpp):成员 + 属性 + 前端逻辑 + 纯虚钩子
class GraphicsDevice {
public:
    void Clear(const Color &color) {
        PlatformClear(ClearOptions::kTarget | ClearOptions::kDepthBuffer | ClearOptions::kStencil,
                      color.ToVector4(), viewport_.MaxDepth(), 0);
    }
    virtual void PlatformClear(ClearOptions, const Vector4 &, float, int) = 0;
    virtual void PlatformPresent() = 0;
    // ...
};

// 平台派生类(Vulkan 后端):override 全部钩子
class VulkanGraphicsDevice : public GraphicsDevice {
    void PlatformClear(ClearOptions, const Vector4 &, float, int) override { /* vk 代码 */ }
};

一个要注意的 C++ 语言映射:基类构造内不能调用纯虚函数(UB)——C# 前端构造里 Setup() → PlatformSetup() 的调用点,在 C++ 改由派生类构造完成后显式调用 PlatformCreate(),语义等价。

MonoGame 鸿蒙移植 - 这篇文章属于一个选集。
§ : 本文

相关文章