From 6908c74efc7c43616d101cf6d23c36efa4a6b6cf Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 29 Mar 2026 22:04:14 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(extensions):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=95=B0=E7=BB=84=E6=89=A9=E5=B1=95=E6=96=B9=E6=B3=95=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E4=BA=8C=E7=BB=B4=E6=95=B0=E7=BB=84=E8=BE=B9=E7=95=8C?= =?UTF-8?q?=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现 IsInBounds 扩展方法用于检查二维数组坐标边界 - 提供泛型支持以适配不同类型的二维数组 - 包含完整的 XML 文档注释说明方法用途和参数 - 遵循 Apache 2.0 开源协议规范添加版权头文件 --- GFramework.Core/Extensions/ArrayExtensions.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 GFramework.Core/Extensions/ArrayExtensions.cs diff --git a/GFramework.Core/Extensions/ArrayExtensions.cs b/GFramework.Core/Extensions/ArrayExtensions.cs new file mode 100644 index 0000000..bc711f9 --- /dev/null +++ b/GFramework.Core/Extensions/ArrayExtensions.cs @@ -0,0 +1,35 @@ +// 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. + +namespace GFramework.Core.Extensions; + +/// +/// 数组扩展方法类,提供二维数组的边界检查等实用功能。 +/// +public static class ArrayExtensions +{ + /// + /// 检查二维数组的给定坐标是否在有效边界内。 + /// + /// 数组元素类型。 + /// 要检查的二维数组。 + /// 要检查的 X 坐标(第一维索引)。 + /// 要检查的 Y 坐标(第二维索引)。 + /// 如果坐标在数组边界内则返回 true;否则返回 false。 + public static bool IsInBounds(this T[,] array, int x, int y) + { + return x >= 0 && y >= 0 && + x < array.GetLength(0) && + y < array.GetLength(1); + } +} \ No newline at end of file From dfbd6a69c5eb3596dcaace26c70fdbd5085e821c Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 29 Mar 2026 22:54:00 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat(ArrayExtensions):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BA=8C=E7=BB=B4=E6=95=B0=E7=BB=84=E6=89=A9=E5=B1=95=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 GetOrDefault 方法用于获取越界时返回默认值的元素 - 添加 GetOr 方法用于获取越界时返回指定回退值的元素 - 添加 TryGet 方法用于尝试获取指定位置的元素 - 添加 GetNeighbors4 方法用于获取四个方向的邻居坐标 - 添加 GetNeighbors8 方法用于获取八个方向的邻居坐标 - 添加 Enumerate 方法用于枚举数组中所有元素及坐标 - 添加 Width 和 Height 方法分别获取数组宽高维度 - 在文件头注释中修复版权年份格式问题 --- GFramework.Core/Extensions/ArrayExtensions.cs | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/GFramework.Core/Extensions/ArrayExtensions.cs b/GFramework.Core/Extensions/ArrayExtensions.cs index bc711f9..9f26b3a 100644 --- a/GFramework.Core/Extensions/ArrayExtensions.cs +++ b/GFramework.Core/Extensions/ArrayExtensions.cs @@ -32,4 +32,137 @@ public static class ArrayExtensions x < array.GetLength(0) && y < array.GetLength(1); } + + + /// + /// 获取二维数组指定位置的元素,如果越界则返回默认值。 + /// + /// 数组元素类型。 + /// 要访问的二维数组。 + /// X 坐标(第一维索引)。 + /// Y 坐标(第二维索引)。 + /// 如果在边界内返回该位置的元素;否则返回类型的默认值。 + public static T? GetOrDefault(this T[,] array, int x, int y) + { + return array.IsInBounds(x, y) ? array[x, y] : default; + } + + /// + /// 获取二维数组指定位置的元素,如果越界则返回指定的回退值。 + /// + /// 数组元素类型。 + /// 要访问的二维数组。 + /// X 坐标(第一维索引)。 + /// Y 坐标(第二维索引)。 + /// 当坐标越界时返回的回退值。 + /// 如果在边界内返回该位置的元素;否则返回指定的回退值。 + public static T GetOr(this T[,] array, int x, int y, T fallback) + { + return array.IsInBounds(x, y) ? array[x, y] : fallback; + } + + /// + /// 尝试获取二维数组指定位置的元素。 + /// + /// 数组元素类型。 + /// 要访问的二维数组。 + /// X 坐标(第一维索引)。 + /// Y 坐标(第二维索引)。 + /// 输出参数,用于存储获取到的元素值。 + /// 如果成功获取元素则返回 true;否则返回 false。 + public static bool TryGet(this T[,] array, int x, int y, out T value) + { + if (array.IsInBounds(x, y)) + { + value = array[x, y]; + return true; + } + + value = default!; + return false; + } + + /// + /// 获取二维数组中某个位置的四个方向邻居坐标(上、下、左、右)。 + /// + /// 数组元素类型。 + /// 源二维数组。 + /// 中心位置的 X 坐标。 + /// 中心位置的 Y 坐标。 + /// 按顺序返回所有在边界内的邻居坐标。 + public static IEnumerable<(int x, int y)> GetNeighbors4(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); + } + } + + /// + /// 获取二维数组中某个位置的八个方向邻居坐标(包括对角线)。 + /// + /// 数组元素类型。 + /// 源二维数组。 + /// 中心位置的 X 坐标。 + /// 中心位置的 Y 坐标。 + /// 按顺序返回所有在边界内的邻居坐标(不包括中心位置)。 + public static IEnumerable<(int x, int y)> GetNeighbors8(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); + } + } + } + + /// + /// 枚举二维数组中的所有元素及其坐标。 + /// + /// 数组元素类型。 + /// 要枚举的二维数组。 + /// 依次返回每个元素的坐标和值的元组。 + public static IEnumerable<(int x, int y, T value)> Enumerate(this T[,] array) + { + for (var x = 0; x < array.GetLength(0); x++) + { + for (var y = 0; y < array.GetLength(1); y++) + { + yield return (x, y, array[x, y]); + } + } + } + + /// + /// 获取二维数组的宽度(第一维长度)。 + /// + /// 数组元素类型。 + /// 要获取宽度的二维数组。 + /// 数组的第一维长度。 + public static int Width(this T[,] array) => array.GetLength(0); + + /// + /// 获取二维数组的高度(第二维长度)。 + /// + /// 数组元素类型。 + /// 要获取高度的二维数组。 + /// 数组的第二维长度。 + public static int Height(this T[,] array) => array.GetLength(1); } \ No newline at end of file From fa82c339924bebf321b5ed22f08f5f18f9fa0cad Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Mon, 30 Mar 2026 08:18:07 +0800 Subject: [PATCH 3/3] =?UTF-8?q?perf(ArrayExtensions):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BA=8C=E7=BB=B4=E6=95=B0=E7=BB=84=E6=89=A9=E5=B1=95=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E6=80=A7=E8=83=BD=E5=B9=B6=E6=B7=BB=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 IsInBounds 和 Enumerate 方法中缓存数组维度长度,避免重复读取元数据 - 为 TryGet 方法添加 MaybeNullWhen 特性注解以改善空值检查 - 更新 TryGet 方法文档说明其在失败时返回默认值的行为 - 新增 ArrayExtensionsTests 类,包含 TryGet 和 Enumerate 方法的完整测试用例 - 添加 using System.Diagnostics.CodeAnalysis 引入空值相关特性支持 --- .../Extensions/ArrayExtensionsTests.cs | 111 ++++++++++++++++++ GFramework.Core/Extensions/ArrayExtensions.cs | 24 +++- 2 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 GFramework.Core.Tests/Extensions/ArrayExtensionsTests.cs diff --git a/GFramework.Core.Tests/Extensions/ArrayExtensionsTests.cs b/GFramework.Core.Tests/Extensions/ArrayExtensionsTests.cs new file mode 100644 index 0000000..928dff9 --- /dev/null +++ b/GFramework.Core.Tests/Extensions/ArrayExtensionsTests.cs @@ -0,0 +1,111 @@ +namespace GFramework.Core.Tests.Extensions; + +/// +/// 测试 ArrayExtensions 扩展方法的关键行为与边界语义。 +/// +[TestFixture] +public class ArrayExtensionsTests +{ + /// + /// 验证 TryGet 在坐标有效时返回 true,并输出目标位置的元素值。 + /// + [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)); + }); + } + + /// + /// 验证 TryGet 在值类型越界时返回 false,并将输出值重置为该类型的默认值。 + /// + [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))); + }); + } + + /// + /// 验证 TryGet 在引用类型越界时返回 false,并将输出值设置为 null。 + /// + [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); + }); + } + + /// + /// 验证 Enumerate 会按第一维优先、第二维递增的顺序返回所有坐标和值。 + /// + [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) + })); + } +} \ No newline at end of file diff --git a/GFramework.Core/Extensions/ArrayExtensions.cs b/GFramework.Core/Extensions/ArrayExtensions.cs index 9f26b3a..014d444 100644 --- a/GFramework.Core/Extensions/ArrayExtensions.cs +++ b/GFramework.Core/Extensions/ArrayExtensions.cs @@ -11,6 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System.Diagnostics.CodeAnalysis; + namespace GFramework.Core.Extensions; /// @@ -28,9 +30,13 @@ public static class ArrayExtensions /// 如果坐标在数组边界内则返回 true;否则返回 false。 public static bool IsInBounds(this T[,] array, int x, int y) { + // 在热路径中缓存维度长度,避免每次比较都重复读取数组元数据。 + var width = array.GetLength(0); + var height = array.GetLength(1); + return x >= 0 && y >= 0 && - x < array.GetLength(0) && - y < array.GetLength(1); + x < width && + y < height; } @@ -68,9 +74,11 @@ public static class ArrayExtensions /// 要访问的二维数组。 /// X 坐标(第一维索引)。 /// Y 坐标(第二维索引)。 - /// 输出参数,用于存储获取到的元素值。 + /// + /// 输出参数,用于存储获取到的元素值;当返回 false 时,该参数会被赋值为 default(T)。 + /// /// 如果成功获取元素则返回 true;否则返回 false。 - public static bool TryGet(this T[,] array, int x, int y, out T value) + public static bool TryGet(this T[,] array, int x, int y, [MaybeNullWhen(false)] out T value) { if (array.IsInBounds(x, y)) { @@ -141,9 +149,13 @@ public static class ArrayExtensions /// 依次返回每个元素的坐标和值的元组。 public static IEnumerable<(int x, int y, T value)> Enumerate(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]); }