在 C# 中,创建一个不可变的 List
(即创建后无法修改)可以通过以下几种方法实现:
1. 使用 System.Collections.Immutable
命名空间
.NET 提供了 System.Collections.Immutable
包,其中包含不可变集合,包括 ImmutableList<T>
。
步骤
- 安装 NuGet 包:
System.Collections.Immutable
- 使用
ImmutableList<T>
类。
示例代码
using System.Collections.Immutable;
class Program
{
static void Main()
{
// 创建一个不可变列表
var immutableList = ImmutableList.Create<int>(1, 2, 3, 4);
// 无法直接修改列表内容
// immutableList.Add(5); // 编译错误
// 新增或移除元素会创建一个新列表
var newList = immutableList.Add(5);
Console.WriteLine(string.Join(", ", newList)); // 输出: 1, 2, 3, 4, 5
}
}
特点
- 不可变集合创建后不能直接修改。
- 修改操作会返回一个新的不可变集合。
- 保证线程安全。
2. 使用 ReadOnlyCollection<T>
ReadOnlyCollection<T>
是一个包装器,提供只读视图的集合。
示例代码
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
class Program
{
static void Main()
{
var list = new List<int> { 1, 2, 3, 4 };
// 创建只读集合
var readOnlyList = new ReadOnlyCollection<int>(list);
// readOnlyList[0] = 10; // 编译错误:只读集合不能被修改
Console.WriteLine(string.Join(", ", readOnlyList)); // 输出: 1, 2, 3, 4
}
}
特点
- 对底层集合的更改会影响到
ReadOnlyCollection<T>
。 - 若需要真正不可变的列表,则应禁止对底层集合的修改。
3. 返回只读接口 IReadOnlyList<T>
使用 IReadOnlyList<T>
接口创建只读访问的集合。
示例代码
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var list = new List<int> { 1, 2, 3, 4 };
// 返回只读接口
IReadOnlyList<int> readOnlyList = list;
// readOnlyList[0] = 10; // 编译错误:只读接口不能被修改
Console.WriteLine(string.Join(", ", readOnlyList)); // 输出: 1, 2, 3, 4
}
}
特点
- 提供只读接口,防止通过此接口修改集合。
- 底层集合可变,因此需要避免直接操作原集合。
4. 使用自定义封装实现真正不可变 List
通过封装类隐藏修改方法,确保列表不可变。
示例代码
using System;
using System.Collections.Generic;
class ImmutableList<T>
{
private readonly List<T> _list;
public ImmutableList(IEnumerable<T> items)
{
_list = new List<T>(items);
}
public T this[int index] => _list[index];
public int Count => _list.Count;
public IEnumerable<T> Items => _list.AsReadOnly();
// 不提供 Add、Remove 等修改方法
}
class Program
{
static void Main()
{
var immutableList = new ImmutableList<int>(new[] { 1, 2, 3, 4 });
Console.WriteLine(string.Join(", ", immutableList.Items)); // 输出: 1, 2, 3, 4
}
}
特点
- 确保底层集合不会被修改。
- 可自定义扩展和控制逻辑。
5. 通过返回副本来避免修改
将操作封装成返回新列表的模式。
示例代码
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var list = new List<int> { 1, 2, 3, 4 };
// 创建副本
var immutableList = new List<int>(list);
// 不直接暴露原始列表
Console.WriteLine(string.Join(", ", immutableList)); // 输出: 1, 2, 3, 4
}
}
特点
- 每次操作都基于副本,避免直接修改。
- 开销可能较高。
总结
- 推荐方法: 使用
ImmutableList<T>
,因其线程安全且性能优化。 - 简单需求: 使用
ReadOnlyCollection<T>
或IReadOnlyList<T>
提供只读访问。 - 完全控制: 自定义封装类实现不可变逻辑。