C# Singleton stackoverflow

本文关键字:stackoverflow Singleton | 更新日期: 2023-09-27 18:31:46

我正在尝试制作一个使用状态机的游戏。"GameState"通过从"World"类(单例)调用getInstance()来创建世界(在进入时)。

世界单例有一个包含"平铺"对象的 2D 列表(世界由图块组成)。在 World 类的构造函数中,列表由嵌套的 for 循环填充,该循环使用 SimpleTileFactory 类创建磁贴并将它们放入列表中。

问题是我得到了一个StackOverflow。我调试了代码,发现 World 单例的构造函数被调用了不止一次,这可能会导致堆栈溢出。我找不到为什么它被调用不止一次,并且感觉我忽略了一些简单的东西。我的单例正确吗?

守则如下。

提前谢谢。

游戏状态类

public partial class GameState : UserControl, IState<MainView>
{
    private bool ready = false;
    public GameState()
    {
        InitializeComponent();
    }
    public void enter(MainView owner)
    {
        this.Width = owner.Width;
        this.Height = owner.Height;
        this.Location = new Point(0, 0);
        owner.Controls.Add(this);
        World.getInstance();
        this.ready = true;
    }
    public void update(MainView owner)
    {
        this.Refresh();
    }
    public void exit(MainView owner)
    {
        owner.Controls.Remove(this);
    }
    public bool isReady()
    {
        return this.ready;
    }
    private void GameState_Paint(object sender, PaintEventArgs e)
    {
        //for (int i = 0; i < World.getInstance().tiles.Count; i++)
        //{
        //    for (int j = 0; j < World.getInstance().tiles[0].Count; j++)
        //    {
        //        //e.Graphics.DrawImage(new Image(), new Rectangle());
        //    }
        //}
    }
}

世界级

class World
{
    private static World instance;
    public GameResources gameResources;
    public SimpleTileFactory tileFactory;
    public List<List<Tile>> tiles = new List<List<Tile>>();
    public Size worldSize = new Size(100, 100);
    private World()
    {
        this.gameResources = new GameResources();
        this.tileFactory = new SimpleTileFactory();
        for (int i = 0; i < worldSize.Height; i++)
        {
            List<Tile> row = new List<Tile>();
            for (int j = 0; j < worldSize.Width; j++)
            {
                row.Add(tileFactory.createTile(new Point(j, i)));
            }
        }
    }

    public static World getInstance()
    {
        if (instance == null)
        {
            instance = new World();
        }
        return instance;
    }
}

简单瓷砖工厂类

class SimpleTileFactory
{
    public SimpleResourceFactory resourceFactory = new SimpleResourceFactory();
    private Random random = new Random();
    private int maxNumOfResourcesPerTile = 5;
    public SimpleTileFactory()
    {
    }

    public Tile createTile(Point location)
    {
        //create tile
        Tile tile = new Tile();
        tile.location = location;
        //give tile terrain type
        Terrain terrain = new Terrain();
        terrain.type = random.Next(0, World.getInstance().gameResources.terrainNames.Count);
        terrain.name = World.getInstance().gameResources.terrainNames[terrain.type];
        terrain.accessability = 1000;
        //add resources to terrain
        int numberOfResources = random.Next(0, maxNumOfResourcesPerTile + 1);
        for (int i = 0; i < numberOfResources; i++)
        {
            terrain.resources.Add(resourceFactory.createTerrainResource(terrain.type));
        }
        tile.terrain = terrain;
        return tile;
    }
}

简单资源工厂类

class SimpleResourceFactory
{
    public List<string> resourceNames = new List<string>();
    public List<Image> resourceImages = new List<Image>();
    private Random random = new Random();
    public SimpleResourceFactory()
    {
        resourceNames.Add("asdfasfd");
        resourceNames.Add("asdfasfd");
        resourceNames.Add("asdfasfd");
    }

    public TerrainResource createTerrainResource(int terrainType)
    {
        TerrainResource resource = new TerrainResource();
        resource.type = random.Next(0, resourceNames.Count);
        resource.name = this.resourceNames[resource.type];
        resource.amount = 100;
        return resource;
    }
}

磁贴类

class Tile
{
    public Point location;
    public Terrain terrain;
    public Tile()
    {
    }
}

地形类

class Terrain
{
    public int type;
    public string name;
    public int accessability;
    public List<TerrainResource> resources = new List<TerrainResource>();
    public Terrain()
    {
    }
}

地形资源类

class TerrainResource
{
    public int type;
    public string name;
    public int amount;
    public TerrainResource()
    {
    }
}

C# Singleton stackoverflow

createTile正在调用getInstance,并导致溢出:

WPF应用程序2.exe!WpfApplication2.SimpleResourceFactory.SimpleResourceFactory() Line 116 C# WPF应用程序2.exe!WpfApplication2.SimpleTileFactory.SimpleTileFactory() Line 76 C# WPF应用程序2.exe!WpfApplication2.World.World() 第 49 行 C# WPF应用程序2.exe!WpfApplication2.World.getInstance() 第 66 行 C# WPF应用程序2.exe!WpfApplication2.SimpleTileFactory.createTile(System.Windows.Point location) 第 95 行 C# WPF应用程序2.exe!WpfApplication2.World.World() 第 56 行 C# WPF应用程序2.exe!WpfApplication2.World.getInstance() 第 66 行 C# WPF应用程序2.exe!WpfApplication2.SimpleTileFactory.createTile(System.Windows.Point location) 第 95 行 C# WPF应用程序2.exe!WpfApplication2.World.World() 第 56 行 C# WPF应用程序2.exe!WpfApplication2.World.getInstance() 第 66 行 C# WPF应用程序2.exe!WpfApplication2.SimpleTileFactory.createTile(System.Windows.Point location) 第 95 行 C#

问题是 createTile() 是在 World 构造函数中调用的。这样,当 createTile() 被执行时,World 对象还没有完全创建,然后它会调用 getInstance(),而 getInstance() 反过来又会创建一个新的世界(因为它还没有完全创建),它再次调用 createTile()。这是一个无限循环。

我做了一个快速修复,通过更改 World 构造函数和 createTile() 代码来检查它,如下所示。这不是解决它最美丽的方法,所以我可能只是将游戏资源与世界分开。

    private World()
    {
        this.gameResources = new GameResources();
        this.tileFactory = new SimpleTileFactory();
        for (int i = 0; i < this.worldSize.Height; i++)
        {
            List<Tile> row = new List<Tile>();
            for (int j = 0; j < this.worldSize.Width; j++)
            {
                row.Add(tileFactory.createTile(new Point(j, i), this));
            }
            this.tiles.Add(row);
        }
    }

而createTile方法:

    public Tile createTile(Point location, World world)
    {
        //create tile
        Tile tile = new Tile();
        tile.location = location;
        //give tile terrain type
        Terrain terrain = new Terrain();
        terrain.type = random.Next(0, world.gameResources.terrainNames.Count);
        terrain.name = world.gameResources.terrainNames[terrain.type];
        terrain.accessability = 1000;
        //add resources to terrain
        int numberOfResources = random.Next(0, maxNumOfResourcesPerTile + 1);
        for (int i = 0; i < numberOfResources; i++)
        {
            terrain.resources.Add(resourceFactory.createTerrainResource(terrain.type));
        }
        tile.terrain = terrain;
        return tile;
    }