gpt4 book ai didi

c# - 如何确保泛型的不变性

转载 作者:太空狗 更新时间:2023-10-29 23:07:03 25 4
gpt4 key购买 nike

这个例子是用 C# 写的,但这个问题确实适用于任何 OO 语言。我想创建一个通用的、不可变的类来实现 IReadOnlyList。此外,此类应具有无法修改的基础通用 IList。最初,这个类是这样写的:

public class Datum<T> : IReadOnlyList<T>
{
private IList<T> objects;
public int Count
{
get;
private set;
}
public T this[int i]
{
get
{
return objects[i];
}
private set
{
this.objects[i] = value;
}
}

public Datum(IList<T> obj)
{
this.objects = obj;
this.Count = obj.Count;
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return this.objects.GetEnumerator();
}
}

然而,这并不是一成不变的。您可能会说,更改初始 IList“obj”会更改 Datum 的“对象”。

static void Main(string[] args)
{
List<object> list = new List<object>();
list.Add("one");
Datum<object> datum = new Datum<object>(list);
list[0] = "two";
Console.WriteLine(datum[0]);
}

这会将“二”写入控制台。由于 Datum 的要点是不变性,这是不行的。为了解决这个问题,我重写了 Datum 的构造函数:

public Datum(IList<T> obj)
{
this.objects = new List<T>();
foreach(T t in obj)
{
this.objects.Add(t);
}
this.Count = obj.Count;
}

给出与之前相同的测试,“一”出现在控制台上。伟大的。但是,如果 Datum 包含一个非不可变集合的集合并且其中一个非不可变集合被修改了怎么办?

static void Main(string[] args)
{
List<object> list = new List<object>();
List<List<object>> containingList = new List<List<object>>();
list.Add("one");
containingList.Add(list);
Datum<List<object>> d = new Datum<List<object>>(containingList);
list[0] = "two";
Console.WriteLine(d[0][0]);
}

而且,正如预期的那样,“two”被打印在控制台上。所以,我的问题是,如何让这个类真正不可变?

最佳答案

你不能。或者更确切地说,你不想,因为这样做的方法太糟糕了。这里有一些:

1。 struct -只有

添加where T : struct给你的Datum<T>类(class)。 structusually immutable , 但如果它包含可变 class实例,它仍然可以修改(感谢 Servy)。主要的缺点是所有的类都被淘汰了,即使是像string这样的不可变类也是如此。以及您创建的任何不可变类。

var e = new ExtraEvilStruct();
e.Mutable = new Mutable { MyVal = 1 };
Datum<ExtraEvilStruct> datum = new Datum<ExtraEvilStruct>(new[] { e });
e.Mutable.MyVal = 2;
Console.WriteLine(datum[0].Mutable.MyVal); // 2

2。创建接口(interface)

创建一个 marker interface并在您创建的任何不可变类型上实现它。主要的缺点是所有内置类型都被淘汰了。而且您真的不知道实现它的类是否真正不可变。

public interface IImmutable
{
// this space intentionally left blank, except for this comment
}
public class Datum<T> : IReadOnlyList<T> where T : IImmutable

3。连载!

如果您序列化和反序列化您传递的对象(例如使用 Json.NET ),您可以创建它们的完全独立的副本。好处:适用于许多您可能想放在此处的内置和自定义类型。缺点:需要额外的时间和内存来创建只读列表,并且要求您的对象是可序列化的而不会丢失任何重要的东西。预计任何指向您列表之外的对象的链接都会被销毁。

public Datum(IList<T> obj)
{
this.objects =
JsonConvert.DeserializeObject<IList<T>>(JsonConvert.SerializeObject(obj));
this.Count = obj.Count;
}

我建议您只记录 Datum<T>说该类应该只用于存储不可变类型。这种未强制执行的隐式要求存在于其他类型中(例如 Dictionary 期望 TKey 以预期的方式实现 GetHashCodeEquals,包括不可变性),因为它很难不那样做。

关于c# - 如何确保泛型的不变性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27322596/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com