perf(ArrayExtensions): 优化二维数组扩展方法性能并添加单元测试

- 在 IsInBounds 和 Enumerate 方法中缓存数组维度长度,避免重复读取元数据
- 为 TryGet 方法添加 MaybeNullWhen 特性注解以改善空值检查
- 更新 TryGet 方法文档说明其在失败时返回默认值的行为
- 新增 ArrayExtensionsTests 类,包含 TryGet 和 Enumerate 方法的完整测试用例
- 添加 using System.Diagnostics.CodeAnalysis 引入空值相关特性支持
This commit is contained in:
GeWuYou 2026-03-30 08:18:07 +08:00
parent dfbd6a69c5
commit fa82c33992
2 changed files with 129 additions and 6 deletions

View 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)
}));
}
}

View File

@ -11,6 +11,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
using System.Diagnostics.CodeAnalysis;
namespace GFramework.Core.Extensions; namespace GFramework.Core.Extensions;
/// <summary> /// <summary>
@ -28,9 +30,13 @@ public static class ArrayExtensions
/// <returns>如果坐标在数组边界内则返回 true否则返回 false。</returns> /// <returns>如果坐标在数组边界内则返回 true否则返回 false。</returns>
public static bool IsInBounds<T>(this T[,] array, int x, int y) 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 && return x >= 0 && y >= 0 &&
x < array.GetLength(0) && x < width &&
y < array.GetLength(1); y < height;
} }
@ -68,9 +74,11 @@ public static class ArrayExtensions
/// <param name="array">要访问的二维数组。</param> /// <param name="array">要访问的二维数组。</param>
/// <param name="x">X 坐标(第一维索引)。</param> /// <param name="x">X 坐标(第一维索引)。</param>
/// <param name="y">Y 坐标(第二维索引)。</param> /// <param name="y">Y 坐标(第二维索引)。</param>
/// <param name="value">输出参数,用于存储获取到的元素值。</param> /// <param name="value">
/// 输出参数,用于存储获取到的元素值;当返回 false 时,该参数会被赋值为 <c>default(T)</c>。
/// </param>
/// <returns>如果成功获取元素则返回 true否则返回 false。</returns> /// <returns>如果成功获取元素则返回 true否则返回 false。</returns>
public static bool TryGet<T>(this T[,] array, int x, int y, out T value) public static bool TryGet<T>(this T[,] array, int x, int y, [MaybeNullWhen(false)] out T value)
{ {
if (array.IsInBounds(x, y)) if (array.IsInBounds(x, y))
{ {
@ -141,9 +149,13 @@ public static class ArrayExtensions
/// <returns>依次返回每个元素的坐标和值的元组。</returns> /// <returns>依次返回每个元素的坐标和值的元组。</returns>
public static IEnumerable<(int x, int y, T value)> Enumerate<T>(this T[,] array) public static IEnumerable<(int x, int y, T value)> Enumerate<T>(this T[,] array)
{ {
for (var x = 0; x < array.GetLength(0); x++) // 缓存维度长度,确保双层循环不会在每次迭代时重复读取数组元数据。
var width = array.GetLength(0);
var height = array.GetLength(1);
for (var x = 0; x < width; x++)
{ {
for (var y = 0; y < array.GetLength(1); y++) for (var y = 0; y < height; y++)
{ {
yield return (x, y, array[x, y]); yield return (x, y, array[x, y]);
} }