c# - 如何将 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# 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;
}
}
笔记:
- 因为你不能拥有
out ref Tile output
(即没有引用引用参数),你必须交换Tile
和bool
值,返回类型与参数。 - 由于您要返回一个引用,因此您必须有一个可以实际引用的值,因此该
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 功能完成你想要的。
推荐阅读
- macos - 如何以编程方式在 macOS 上验证用户和密码?
- zooming - 当codeceptjs pressKey失败时如何全屏查看?
- sql - SQL:如何获取可用的最早日期,如果没有日期则设置为字符串
- kubernetes - Kubernetes:尝试通过 kube-mgmt 加载 OPA 策略,方法是创建一个包含策略的配置映射,但配置映射没有任何注释
- python - 使用 Python 根据文件名中的日期将文件分组到列表中
- java - Jenkins 和 Nexus:如何上传 javadoc 和源代码?
- linux - Linux上的VALA中带有图标、文本和两个按钮的简单对话框(基本操作系统)
- filefilter - 尝试使用 FileFilter 时出现 java.lang.NoClassDeffoundError
- python - 甲板洗牌算法不够“人性化”
- ajax - 如何将 formcollection 转换为模型 ins ASP.NET MVC