ArrayPoolWrapper简洁、安全的ArrayPool

通过.NET中的 ArrayPool 我们可以实现对T[]类型的池化,避免频繁的分配内存和GC,以提升性能。鉴于已有不少博客介绍ArrayPool的具体原理,本文不会涉及其实现细节。本文聚焦使用中的痛点,并提供简洁的封装方案以提升ArrarPool使用的便捷性。

ArrayPool本身的使用方式比较简单:

复制代码
using System.Buffers;
​
var pool = ArrayPool<int>.Shared.Rent(4);
// 其他逻辑
ArrayPool<int>.Shared.Return(pool);

为了确保在发生异常时能够释放资源,通常需要写成如下形式的样板代码:

复制代码
int[] pool = null!;
try
{
    pool = ArrayPool<int>.Shared.Rent(4);
    // 其他逻辑
}
finally
{
    if (pool != null)
    {
        ArrayPool<int>.Shared.Return(pool);
    }
}

以上写法会是我们的代码中充斥大量的样板代码和大量的嵌套,影响代码后续的可读性和可维护性。

接下来我们在原ArrayPool的基础上稍加封装,以实现简洁、安全的使用ArrayPool的目标,代码如下:

复制代码
public struct ArrayPoolWrapper<T> : IDisposable
{
    private int _index = -1;
    private bool _disposed = false;
    private readonly int _capacity;
​
    private readonly T[] _pool;
​
    public ArrayPoolWrapper(int capacity)
    {
        if (capacity <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(capacity), "The capacity must be greater than 0.");
        }
​
        this._capacity = capacity;
        _pool = ArrayPool<T>.Shared.Rent(capacity);
    }
​
​
    public void Add(T info)
    {
        ThrowIfDisposed();
​
        _index++;
        if (_index >= _capacity)
        {
            _index--;
​
            throw new InvalidOperationException("The array pool has reached its capacity.");
        }
​
        _pool[_index] = info;
    }
​
    public void Dispose()
    {
        ThrowIfDisposed();
        _disposed = true;
​
        ArrayPool<T>.Shared.Return(_pool);
    }
​
​
    private readonly void ThrowIfDisposed()
    {
        if (_disposed)
        {
            throw new ObjectDisposedException(nameof(ArrayPoolWrapper<T>));
        }
    }
}

封装后的使用只需一行代码,效果如下:

复制代码
using var pool = new ArrayPoolWrapper<int>(5);

我们还可以通过封装来实现更多的扩展API,如:RemoveLastOne以及基于Span的切片操作:

复制代码
public struct ArrayPoolWrapper<T> : IDisposable
{
    public readonly int Count => _index + 1;
    public readonly Span<T> Values => _pool.AsSpan()[..Count];
​
​
    public void RemoveLastOne()
    {
        ThrowIfDisposed();
​
        if (Count <= 0)
        {
            throw new InvalidOperationException("The array pool is empty.");
        }
​
        _pool[_index] = default!;
        _index--;
    }
}

使用示例如下:

复制代码
using var pool = new ArrayPoolWrapper<int>(8);
for (var i = 0; i < 8; i++)
{
    pool.Add(i);
}
​
pool.RemoveLastOne();
Console.WriteLine(pool.Count);
​
foreach (var i in pool.Values[1..3])
{
    Console.WriteLine(i);
} 

完整的实现代码已在Github上开源。

相关推荐
海盗12342 小时前
C#上位机开发-S7协议通信
开发语言·c#
FeBaby2 小时前
ReentrantLock 与 synchronized 底层实现对比图解
开发语言·c#
烟话64 小时前
C# 内存机制详解:值类型、引用类型与 String 的不可变性
java·jvm·c#
我是唐青枫4 小时前
C#.NET MemoryMarshal 深入解析:零拷贝内存重解释、二进制读写与使用边界
c#·.net
步步为营DotNet4 小时前
深入剖析.NET 11 中 Semantic Kernel 于智能后端集成的创新实践
前端·.net·easyui
susan花雨5 小时前
C#异步并行处理的用法
c#
喵叔哟6 小时前
10.【.NET10 实战--孢子记账--产品智能化】--其余第三方包批量升级
人工智能·.net
weixin_520649876 小时前
Winform创建与启动
开发语言·c#
武藤一雄6 小时前
WPF中逻辑树(Logical Tree)与可视化树(Visual Tree)到底是什么
microsoft·c#·.net·wpf·.netcore