如何在C#中让自定义类Foo可像二维数组(int?[,])一样使用?
嘿,我懂你想要的效果——让Foo实例直接像二维数组那样被操作,不用每次都敲.Value来访问内部的数组,对吧?这确实属于包装器的典型场景,我们可以通过实现几个关键接口和方法来搞定,不止是IEnumerable,还能让它支持数组的核心操作,下面一步步给你讲清楚:
解决方案:让Foo类模拟二维数组的行为
要让Foo完全具备二维数组的使用体验,我们需要实现三个核心部分:枚举支持、长度获取、索引访问,逐个来看:
1. 实现IEnumerable<int?>接口
这是让Foo支持LINQ方法(比如First、Last、ToArray)的关键。注意必须同时实现泛型和非泛型的枚举接口,否则部分LINQ操作会失效:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; public class Foo : IEnumerable<int?> { public int?[,] Value { get; private set; } = new int?[3, 9]; public Foo() => Value = InternalGenerate(); public static Foo Generate() => new() { Value = InternalGenerate() }; protected static int?[,] InternalGenerate() { // 这里替换成你实际的生成逻辑,下面是个测试用的示例 var ticket = new int?[3,9]; ticket[0,0] = 1; ticket[0,8] = 9; ticket[2,0] = 10; ticket[2,8] = 18; return ticket; } // 泛型枚举器,遍历内部二维数组的所有元素 public IEnumerator<int?> GetEnumerator() { foreach (var item in Value) { yield return item; } } // 必须显式实现非泛型的IEnumerable接口 IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
2. 添加GetLength方法
为了让你能直接调用foo.GetLength(0)而不是foo.Value.GetLength(0),直接在Foo类里封装这个方法:
public int GetLength(int dimension) { return Value.GetLength(dimension); }
3. 添加索引器(可选,但更贴近数组体验)
如果想直接用foo[row, col]的方式访问元素,就添加这个索引器:
public int? this[int row, int col] { get => Value[row, col]; private set => Value[row, col] = value; }
测试修改后的代码
现在你的调用代码完全可以去掉.Value,直接用Foo实例操作:
class Program { static void Main() { Bar(new Foo()); } static void Bar(Foo foo) { int width = foo.GetLength(1); int height = foo.GetLength(0); int?[] array = foo.ToArray(); // 现在不用Cast了,直接调用ToArray // 获取四个角的值 int tl = (int)array.First(e => e.HasValue); int tr = (int)array.Take(width).Last(e => e.HasValue); int bl = (int)array.Skip((height - 1) * width).First(e => e.HasValue); int br = (int)array.Last(e => e.HasValue); Console.WriteLine($"Top Left: {tl}, Top Right: {tr}"); Console.WriteLine($"Bottom Left: {bl}, Bottom Right: {br}"); } }
为什么之前的IEnumerable尝试没成功?
大概率是因为你只实现了泛型的IEnumerable<T>,但没显式实现非泛型的IEnumerable接口——C#要求这两个接口必须同时实现,否则某些集合操作或LINQ方法无法识别你的Foo实例是可枚举的。
这样修改后,你的Foo类就完全可以当作二维数组的包装器来使用,不用每次都写.Value,完美匹配你的需求!
内容的提问来源于stack exchange,提问作者just-a-hriday




