You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在C#中让自定义类Foo可像二维数组(int?[,])一样使用?

嘿,我懂你想要的效果——让Foo实例直接像二维数组那样被操作,不用每次都敲.Value来访问内部的数组,对吧?这确实属于包装器的典型场景,我们可以通过实现几个关键接口和方法来搞定,不止是IEnumerable,还能让它支持数组的核心操作,下面一步步给你讲清楚:

解决方案:让Foo类模拟二维数组的行为

要让Foo完全具备二维数组的使用体验,我们需要实现三个核心部分:枚举支持、长度获取、索引访问,逐个来看:

1. 实现IEnumerable<int?>接口

这是让Foo支持LINQ方法(比如FirstLastToArray)的关键。注意必须同时实现泛型和非泛型的枚举接口,否则部分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

火山引擎 最新活动