mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-31 18:27:14 +08:00
Merge pull request #150 from GeWuYou/feat/array-extensions-2d-helper-methods-
Feat/array extensions 2d helper methods
This commit is contained in:
commit
63cb6b5e17
111
GFramework.Core.Tests/Extensions/ArrayExtensionsTests.cs
Normal file
111
GFramework.Core.Tests/Extensions/ArrayExtensionsTests.cs
Normal file
@ -0,0 +1,111 @@
|
||||
namespace GFramework.Core.Tests.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// 测试 ArrayExtensions 扩展方法的关键行为与边界语义。
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class ArrayExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证 TryGet 在坐标有效时返回 true,并输出目标位置的元素值。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TryGet_Should_Return_True_And_Assign_Value_When_Coordinates_Are_In_Bounds()
|
||||
{
|
||||
// Arrange
|
||||
var array = new[,]
|
||||
{
|
||||
{ 1, 2 },
|
||||
{ 3, 4 }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = array.TryGet(1, 0, out var value);
|
||||
|
||||
// Assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(value, Is.EqualTo(3));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 TryGet 在值类型越界时返回 false,并将输出值重置为该类型的默认值。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TryGet_Should_Return_False_And_Default_Value_When_Value_Type_Is_Out_Of_Bounds()
|
||||
{
|
||||
// Arrange
|
||||
var array = new[,]
|
||||
{
|
||||
{ 1, 2 },
|
||||
{ 3, 4 }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = array.TryGet(2, 0, out int value);
|
||||
|
||||
// Assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(result, Is.False);
|
||||
Assert.That(value, Is.EqualTo(default(int)));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 TryGet 在引用类型越界时返回 false,并将输出值设置为 null。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TryGet_Should_Return_False_And_Null_When_Reference_Type_Is_Out_Of_Bounds()
|
||||
{
|
||||
// Arrange
|
||||
var array = new string[,]
|
||||
{
|
||||
{ "A", "B" },
|
||||
{ "C", "D" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = array.TryGet(-1, 0, out string? value);
|
||||
|
||||
// Assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(result, Is.False);
|
||||
Assert.That(value, Is.Null);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 Enumerate 会按第一维优先、第二维递增的顺序返回所有坐标和值。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Enumerate_Should_Return_All_Coordinates_And_Values_In_Deterministic_Order()
|
||||
{
|
||||
// Arrange
|
||||
var array = new[,]
|
||||
{
|
||||
{ 10, 20, 30 },
|
||||
{ 40, 50, 60 }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = array.Enumerate().ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.That(
|
||||
result,
|
||||
Is.EqualTo(
|
||||
new (int x, int y, int value)[]
|
||||
{
|
||||
(0, 0, 10),
|
||||
(0, 1, 20),
|
||||
(0, 2, 30),
|
||||
(1, 0, 40),
|
||||
(1, 1, 50),
|
||||
(1, 2, 60)
|
||||
}));
|
||||
}
|
||||
}
|
||||
180
GFramework.Core/Extensions/ArrayExtensions.cs
Normal file
180
GFramework.Core/Extensions/ArrayExtensions.cs
Normal file
@ -0,0 +1,180 @@
|
||||
// Copyright (c) 2026 GeWuYou
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace GFramework.Core.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// 数组扩展方法类,提供二维数组的边界检查等实用功能。
|
||||
/// </summary>
|
||||
public static class ArrayExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 检查二维数组的给定坐标是否在有效边界内。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型。</typeparam>
|
||||
/// <param name="array">要检查的二维数组。</param>
|
||||
/// <param name="x">要检查的 X 坐标(第一维索引)。</param>
|
||||
/// <param name="y">要检查的 Y 坐标(第二维索引)。</param>
|
||||
/// <returns>如果坐标在数组边界内则返回 true;否则返回 false。</returns>
|
||||
public static bool IsInBounds<T>(this T[,] array, int x, int y)
|
||||
{
|
||||
// 在热路径中缓存维度长度,避免每次比较都重复读取数组元数据。
|
||||
var width = array.GetLength(0);
|
||||
var height = array.GetLength(1);
|
||||
|
||||
return x >= 0 && y >= 0 &&
|
||||
x < width &&
|
||||
y < height;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取二维数组指定位置的元素,如果越界则返回默认值。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型。</typeparam>
|
||||
/// <param name="array">要访问的二维数组。</param>
|
||||
/// <param name="x">X 坐标(第一维索引)。</param>
|
||||
/// <param name="y">Y 坐标(第二维索引)。</param>
|
||||
/// <returns>如果在边界内返回该位置的元素;否则返回类型的默认值。</returns>
|
||||
public static T? GetOrDefault<T>(this T[,] array, int x, int y)
|
||||
{
|
||||
return array.IsInBounds(x, y) ? array[x, y] : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取二维数组指定位置的元素,如果越界则返回指定的回退值。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型。</typeparam>
|
||||
/// <param name="array">要访问的二维数组。</param>
|
||||
/// <param name="x">X 坐标(第一维索引)。</param>
|
||||
/// <param name="y">Y 坐标(第二维索引)。</param>
|
||||
/// <param name="fallback">当坐标越界时返回的回退值。</param>
|
||||
/// <returns>如果在边界内返回该位置的元素;否则返回指定的回退值。</returns>
|
||||
public static T GetOr<T>(this T[,] array, int x, int y, T fallback)
|
||||
{
|
||||
return array.IsInBounds(x, y) ? array[x, y] : fallback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取二维数组指定位置的元素。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型。</typeparam>
|
||||
/// <param name="array">要访问的二维数组。</param>
|
||||
/// <param name="x">X 坐标(第一维索引)。</param>
|
||||
/// <param name="y">Y 坐标(第二维索引)。</param>
|
||||
/// <param name="value">
|
||||
/// 输出参数,用于存储获取到的元素值;当返回 false 时,该参数会被赋值为 <c>default(T)</c>。
|
||||
/// </param>
|
||||
/// <returns>如果成功获取元素则返回 true;否则返回 false。</returns>
|
||||
public static bool TryGet<T>(this T[,] array, int x, int y, [MaybeNullWhen(false)] out T value)
|
||||
{
|
||||
if (array.IsInBounds(x, y))
|
||||
{
|
||||
value = array[x, y];
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取二维数组中某个位置的四个方向邻居坐标(上、下、左、右)。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型。</typeparam>
|
||||
/// <param name="array">源二维数组。</param>
|
||||
/// <param name="x">中心位置的 X 坐标。</param>
|
||||
/// <param name="y">中心位置的 Y 坐标。</param>
|
||||
/// <returns>按顺序返回所有在边界内的邻居坐标。</returns>
|
||||
public static IEnumerable<(int x, int y)> GetNeighbors4<T>(this T[,] array, int x, int y)
|
||||
{
|
||||
var dirs = new (int dx, int dy)[]
|
||||
{
|
||||
(0, -1), (0, 1),
|
||||
(-1, 0), (1, 0)
|
||||
};
|
||||
|
||||
foreach (var (dx, dy) in dirs)
|
||||
{
|
||||
var nx = x + dx;
|
||||
var ny = y + dy;
|
||||
|
||||
if (array.IsInBounds(nx, ny))
|
||||
yield return (nx, ny);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取二维数组中某个位置的八个方向邻居坐标(包括对角线)。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型。</typeparam>
|
||||
/// <param name="array">源二维数组。</param>
|
||||
/// <param name="x">中心位置的 X 坐标。</param>
|
||||
/// <param name="y">中心位置的 Y 坐标。</param>
|
||||
/// <returns>按顺序返回所有在边界内的邻居坐标(不包括中心位置)。</returns>
|
||||
public static IEnumerable<(int x, int y)> GetNeighbors8<T>(this T[,] array, int x, int y)
|
||||
{
|
||||
for (var dx = -1; dx <= 1; dx++)
|
||||
{
|
||||
for (var dy = -1; dy <= 1; dy++)
|
||||
{
|
||||
if (dx == 0 && dy == 0) continue;
|
||||
|
||||
var nx = x + dx;
|
||||
var ny = y + dy;
|
||||
|
||||
if (array.IsInBounds(nx, ny))
|
||||
yield return (nx, ny);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 枚举二维数组中的所有元素及其坐标。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型。</typeparam>
|
||||
/// <param name="array">要枚举的二维数组。</param>
|
||||
/// <returns>依次返回每个元素的坐标和值的元组。</returns>
|
||||
public static IEnumerable<(int x, int y, T value)> Enumerate<T>(this T[,] array)
|
||||
{
|
||||
// 缓存维度长度,确保双层循环不会在每次迭代时重复读取数组元数据。
|
||||
var width = array.GetLength(0);
|
||||
var height = array.GetLength(1);
|
||||
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
yield return (x, y, array[x, y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取二维数组的宽度(第一维长度)。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型。</typeparam>
|
||||
/// <param name="array">要获取宽度的二维数组。</param>
|
||||
/// <returns>数组的第一维长度。</returns>
|
||||
public static int Width<T>(this T[,] array) => array.GetLength(0);
|
||||
|
||||
/// <summary>
|
||||
/// 获取二维数组的高度(第二维长度)。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型。</typeparam>
|
||||
/// <param name="array">要获取高度的二维数组。</param>
|
||||
/// <returns>数组的第二维长度。</returns>
|
||||
public static int Height<T>(this T[,] array) => array.GetLength(1);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user