C# 结构是否可以包含引用类型的字段
结构是否可以包含引用类型的字段?如果可以,这是一种不好的做法吗 是的,他们可以 视情况而定 许多人认为结构应该是不可变的,在这种情况下,持有对对象的引用可能意味着它不是C# 结构是否可以包含引用类型的字段,c#,struct,reference-type,C#,Struct,Reference Type,结构是否可以包含引用类型的字段?如果可以,这是一种不好的做法吗 是的,他们可以 视情况而定 许多人认为结构应该是不可变的,在这种情况下,持有对对象的引用可能意味着它不是 但这要视情况而定。这是肯定的,而且这样做并不是不好的做法 struct Example { public readonly string Field1; } 只读不是必需的,但使结构不可变是一种很好的做法。是的,这是可能的,是的,这通常是一种不好的做法 如果您查看.NET framework本身,您将看到几乎所有结构都只包
但这要视情况而定。这是肯定的,而且这样做并不是不好的做法
struct Example {
public readonly string Field1;
}
只读不是必需的,但使结构不可变是一种很好的做法。是的,这是可能的,是的,这通常是一种不好的做法
如果您查看.NET framework本身,您将看到几乎所有结构都只包含基本值类型。是的,它们可以。这是个好主意吗?那要看情况而定。就我个人而言,我很少首先创建自己的结构。。。我会以某种程度的怀疑态度对待任何新的用户定义结构。我并不是说它总是一个错误的选择,只是它需要一个更清晰的论证而不是一个类 对于一个结构来说,引用一个可变的对象是一个坏主意。。。否则,可以有两个看起来独立但不独立的值:
MyValueType foo = ...;
MyValueType bar = foo; // Value type, hence copy...
foo.List.Add("x");
// Eek, bar's list has now changed too!
可变结构是邪恶的。引用可变类型的不可变结构在不同方面都是邪恶的。不能拥有可变结构的原因是引用类型的行为。阅读本文: 当您有一个包含对象的结构(任何不是int或double这样的基元的东西)并复制该结构的实例时,内部的对象不会被“深度”复制,因为它只是对包含实际类的内存位置的引用(指针)。因此,如果您复制一个包含类实例的可变结构,那么该副本将引用与原始实例相同的实例(因此上面更改了bar的列表)
如果你一定要让这个结构是可变的,那就让里面的任何类实例都是只读的,或者——这是一个糟糕的做法——试着确保你永远不会复制这个结构。因为这得到了否决票,我试着重写一下,看看它是否能变得更清晰。这个问题由来已久;但是很好!我最近还看到了一些详细阐述这一点的链接 我想补充的一点是,如果您声明引用字段,您必须能够在自己的块之外进行推理:当有人使用您的结构时。我添加的具体点实际上只是关于声明结构的只读字段;但在这种情况下,结构中的字段可以更改其结果;这很难解释 我看到了这个链接,程序员在其中声明了一个包含
只读结构字段的类。他的类中的字段是一个结构
——它是一个链接列表。枚举器
——由于该字段是只读的
——他自己的类方法得到了枚举器结构的副本,状态是复制的,而不是动态的
但是,如果您继续并通过简单地从struct字段中删除只读
来修复他的代码(这是可行的);然而,如果您决定创建自己的类astruct
,那么现在,您的结构的使用者不能将其用作只读字段,否则他们会被同样的问题所困扰。(如果这似乎是人为的,因为您没有只读枚举器,那么如果它支持重置,您实际上可能会这样做!)
因此,如果这不是一个最清晰的例子,我想说的是,你可以对自己的实现进行推理,但是如果你是一个结构体,你还需要对复制你的价值的消费者以及他们将得到什么进行推理
下面是我找到的例子
他的类不是结构
,但确实包含m_枚举器字段(程序员应该知道它是结构
)
事实证明,该类的方法得到了该值的副本,并且不起作用实际上,您可以非常仔细地检查此块以了解这一点
您可以通过将字段设置为非只读——来解决此问题,因为该字段已经指向令人困惑的地方。但您也可以通过将字段声明为接口
类型--IEnumerator
来修复它
但是,如果您将字段声明为struct
,并声明它不是readonly
,然后选择将类定义为struct
,那么现在如果有人在某个类中将struct
的实例声明为readonly
字段,那么他们又会丢失
例如:
公共类程序
{
私有结构枚举器包装器:IEnumerator
{
//总是失败---本地方法读取只读结构并获取副本
//私有只读链接列表。枚举器m_枚举器;
//修正了一个:---在本地,方法不再获得副本;
//但是如果这个结构的使用者创建了一个只读字段,那么他们也会这样做
//始终获取此字段的副本,并且此字段包含此结构字段的副本!
私有链接列表。枚举器m_枚举器;
//两者都解决了!!
//因为这不是一个值类型,所以即使这个结构的使用者
//只读复制,始终读取内存指针而不是值
//私有IEnumerator m_枚举器;
公共枚举器包装器(LinkedList LinkedList)
=>m_枚举器=linkedList.GetEnumerator();
公共电流
=>m_枚举器.Current;
对象System.Collections.IEnumerator.Current
=>电流;
公共图书馆
=>m_枚举数。MoveNext();
公共无效重置()
=>((System.Collections.IEnumerator)m_枚举器).Reset();
公共空间处置()
=>m_枚举器.Dispose();
}
private readonly LinkedList l=新建LinkedList();
私有只读枚举器;
公共项目
public class Program
{
private struct EnumeratorWrapper : IEnumerator<int>
{
// Fails always --- the local methods read the readonly struct and get a copy
//private readonly LinkedList<int>.Enumerator m_Enumerator;
// Fixes one: --- locally, methods no longer get a copy;
// BUT if a consumer of THIS struct makes a readonly field, then again they will
// always get a copy of THIS, AND this contains a copy of this struct field!
private LinkedList<int>.Enumerator m_Enumerator;
// Fixes both!!
// Because this is not a value type, even a consumer of THIS struct making a
// readonly copy, always reads the memory pointer and not a value
//private IEnumerator<int> m_Enumerator;
public EnumeratorWrapper(LinkedList<int> linkedList)
=> m_Enumerator = linkedList.GetEnumerator();
public int Current
=> m_Enumerator.Current;
object System.Collections.IEnumerator.Current
=> Current;
public bool MoveNext()
=> m_Enumerator.MoveNext();
public void Reset()
=> ((System.Collections.IEnumerator) m_Enumerator).Reset();
public void Dispose()
=> m_Enumerator.Dispose();
}
private readonly LinkedList<int> l = new LinkedList<int>();
private readonly EnumeratorWrapper e;
public Program()
{
for (int i = 0; i < 10; ++i) {
l.AddLast(i);
}
e = new EnumeratorWrapper(l);
}
public static void Main()
{
Program p = new Program();
// This works --- a local copy every time
EnumeratorWrapper e = new EnumeratorWrapper(p.l);
while (e.MoveNext()) {
Console.WriteLine(e.Current);
}
// This fails if the struct cannot support living in a readonly field
while (p.e.MoveNext()) {
Console.WriteLine(p.e.Current);
}
Console.ReadKey();
}
}