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] =?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]);
}