首页 > 解决方案 > 如何将 out 参数用于二维数组中的结构条目?

问题描述

我有一个包含 2D 瓷砖地图的类。我想要一个TryGetTile() 类似于TryGetValue()Dictionary 类的内容。我的瓷砖是结构。

public class Map
{

    Tile[,] tiles = new Tile[100, 100];

    public bool TryGetTile (int x, int y, out Tile output)
    {
        if (x >= 0 && y >= 0 && x < tiles.GetLength(0) && y < tiles.GetLength(1))
        {
            output = tiles[x,y];
            return true;
        }
        output = default(Tile);
        return false;
    }

}

public struct Tile
{
    public int someValue;
}

预期用途类似于

Map someMap = new Map();
if (someMap.TryGetTile(25, 30, out var tile))
{
      tile.someValue += 100;
}

这不会按预期工作,因为返回Tile的实际上不是对该数组条目的引用。我知道结构是按值传递的,但我认为这相当于修改 的引用tiles[25,30],如果这有意义的话。

这将在以后成为一个非问题,因为我也知道结构应该很少是可变的,所以我可能最终会改用类。但是,我很好奇是否有办法使这项工作按需要进行。

标签: c#

解决方案


从 C# 7.0 开始,您可以执行此操作。您应该将这种类型的实现限制在性能关键的场景中,因为它会使代码更难理解,并给代码带来一些尴尬和风险。但可以使用Ref 返回和 ref locals功能来完成:

public class Map
{

    static Tile defaultTile = default(Tile);
    Tile[,] tiles = new Tile[100, 100];

    public ref Tile TryGetTile(int x, int y, out bool found)
    {
        if (x >= 0 && y >= 0 && x < tiles.GetLength(0) && y < tiles.GetLength(1))
        {
            found = true;
            return ref tiles[x, y];
        }

        found = false;
        return ref defaultTile;
    }
}

笔记:

  1. 因为你不能拥有out ref Tile output(即没有引用引用参数),你必须交换Tilebool值,返回类型与参数。
  2. 由于您要返回一个引用,因此您必须有一个可以实际引用的值,因此该defaultTile字段。但小心点!ref是原始变量的别名,这意味着该方法的调用者可以修改该defaultTile字段。呸!(您可以通过在每次返回对它的引用时重新初始化该字段来稍微减轻这种风险,但这会侵蚀您从该策略中获得的任何性能优势。)

示例用法:

Map map = new Map();

// ref return: these variables are aliases to the elements in the array
ref Tile tile1 = ref map.TryGetTile(10, 10, out bool found1),
    tile2 = ref map.TryGetTile(200, 200, out bool found2);

Console.WriteLine($"found1: {found1}");
Console.WriteLine($"found2: {found2}");
tile1.someValue = 17;

// value return: this variable is a *copy* of the element in the array
Tile tile3 = map.TryGetTile(10, 10, out bool found3);

Console.WriteLine($"tile3.someValue: {tile3.someValue}");

这将输出以下内容:

发现1:真
发现2:假
tile3.someValue: 17

即,从数组复制的Tile返回并存储在tile3中的值具有之前通过ref Tile变量设置的值tile1

对于它的价值,我个人的偏好是尽可能使用不可变对象,即使是引用类型。这改变了诸如您的方法之类的事物的语义TryGetTile(),因为调用者永远不能修改对象本身。相反,您的Map类还需要提供一个更新Tile值的方法。Tile除此之外,如果您想要引用类型语义,那么成为引用类型确实更好。

但是如果你真的坚持,你可以通过 ref local 和 ref return 功能完成你想要的。


推荐阅读