You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法调用Window类及空引用异常问题求助

问题分析

你遇到的几个错误根源清晰明了:

  1. System.NullReferenceException:Application类中private Window window;仅声明了字段但未初始化,调用window.CreateWindow()时window为null,自然触发空引用异常。
  2. CS0144:Window是抽象类,C#语法明确禁止直接实例化抽象类或接口。
  3. CS0120:静态方法CreateWindow中调用了非静态的抽象方法Create(),静态方法属于类本身,无法访问仅属于实例的成员。
解决方案

我们通过重构窗口创建逻辑,利用工厂模式正确实例化WindowsWindow子类,同时修复Application中的空引用问题:

1. 修改Window抽象类,实现静态工厂方法

把抽象的实例Create方法改为静态工厂方法,解决"要创建对象但需要先有对象调用方法"的矛盾:

using System;

namespace Jade
{
    public struct WindowProps
    {
        public string Title;
        public uint Width;
        public uint Height;

        public WindowProps(in string title = "Camellia Engine", uint width = 1280, uint height = 720)
        {
            Title = title;
            Width = width;
            Height = height;
        }
    }

    public abstract class Window
    {
        public delegate void EventCallbackFn(Event e);
        
        ~Window() { }
        
        public abstract void OnUpdate();
        public abstract uint GetWidth();
        public abstract uint GetHeight();
        public abstract void SetEventCallback(in EventCallbackFn callback);
        public abstract void SetVSync(bool enabled = false);
        public abstract bool IsVSyncEnabled();

        // 静态工厂方法,直接返回具体平台窗口实例(可扩展多平台判断)
        public static Window Create(WindowProps props = new WindowProps())
        {
            // 这里可添加平台分支判断,比如Windows/macOS/Linux
            return new WindowsWindow(props);
        }
    }
}

2. 调整WindowsWindow类,移除冗余重写

父类的Create改为静态后,子类无需再重写抽象的Create方法,同时修复GCHandle的生命周期问题:

using System;
using GLFW;
using GLFW.Game;
using System.Runtime.InteropServices;

namespace Jade
{
    // 若需跨命名空间访问,将internal改为public
    public class WindowsWindow : Window
    {
        public static bool m_isGLFWInitialized = false;
        private NativeWindow m_window;
        private WindowData m_Data;
        private GCHandle m_dataHandle; // 保存GCHandle,避免指针失效

        public WindowsWindow(WindowProps props) 
        {
            Init(props);
        }
        
        ~WindowsWindow()
        {
            Shutdown();
        }

        public override void OnUpdate()
        {
            Glfw.PollEvents();
            Glfw.SwapBuffers(m_window);
        }

        public override uint GetWidth()
        {
            return m_Data.Width;
        }

        public override uint GetHeight()
        {
            return m_Data.Height;
        }

        public override void SetEventCallback(in EventCallbackFn callback)
        {
            m_Data.EventCallback = callback;
        }

        public override void SetVSync(bool enabled)
        {
            Glfw.SwapInterval(enabled ? 1 : 0);
            m_Data.VSync = enabled;
        }

        public override bool IsVSyncEnabled()
        {
            return m_Data.VSync;
        }
        
        private void Init(in WindowProps props)
        {
            m_Data.Title = props.Title;
            m_Data.Width = props.Width;
            m_Data.Height = props.Height;

            Log.CM_CORE_INFO($"Creating window: {props.Title} ({props.Width}x{props.Height})");

            if (!m_isGLFWInitialized)
            {
                bool success = Glfw.Init();
                m_isGLFWInitialized = true;
            }

            m_window = new GameWindow((int)props.Width, (int)props.Height, m_Data.Title);
            
            // 固定WindowData内存,直到窗口销毁
            m_dataHandle = GCHandle.Alloc(m_Data, GCHandleType.Pinned);
            IntPtr dataPtr = m_dataHandle.AddrOfPinnedObject();

            Glfw.MakeContextCurrent(m_window);
            Glfw.SetWindowUserPointer(m_window, dataPtr);
            SetVSync(true);
        }

        public void Shutdown() 
        {
            Glfw.DestroyWindow(m_window);
            // 释放固定的内存
            if (m_dataHandle.IsAllocated)
                m_dataHandle.Free();
            if (m_isGLFWInitialized)
            {
                Glfw.Terminate();
                m_isGLFWInitialized = false;
            }
        }

        private struct WindowData
        {
            public string Title;
            public uint Width, Height;
            public bool VSync;
            public EventCallbackFn EventCallback;
        };
    }
}

3. 修复Application类的空引用问题

在Application构造函数中,通过Window的静态工厂方法初始化window字段:

namespace Jade
{
    public class Application
    {
        private bool m_Running = true;
        private Window window;

        public Application()
        {
            // 通过静态工厂方法创建窗口实例
            window = Window.Create();
        }

        ~Application() 
        {
            // 销毁窗口资源
            if (window is WindowsWindow windowsWindow)
            {
                windowsWindow.Shutdown();
            }
        }

        public void Run()
        {
            while (m_Running)
            {
                window.OnUpdate();
            }
        }

        public Application CreateApplication()
        {
            return new Sandbox();
        }
    }
}

额外提示

  • 若需多平台支持,可在Window.Create()中加入平台判断逻辑(比如使用OperatingSystem.IsWindows()等API),返回对应平台的窗口子类。
  • 确保Sandbox类(若继承自Application)的构造逻辑不会覆盖window的初始化流程。

内容的提问来源于stack exchange,提问作者Bobi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 03:54:53