结构体可以包含引用类型的字段

时间:2009-06-03 16:19:49

标签: c# struct reference-type

结构体是否包含引用类型的字段?如果可以,这是一个不好的做法?

6 个答案:

答案 0 :(得分:79)

是的,他们可以。这是个好主意吗?那么,这取决于具体情况。就个人而言,我很少首先创建自己的结构...我会对任何新的用户定义结构进行一定程度的怀疑。我并不是说它总是总是错误的选项,只是它需要更多的明确的参数而不是类。

虽然结构有一个可变对象的引用,但这是一个坏主意...否则你可以有两个看起来独立但不是的值:

MyValueType foo = ...;
MyValueType bar = foo; // Value type, hence copy...

foo.List.Add("x");
// Eek, bar's list has now changed too!

可变结构是邪恶的。引用可变类型的不可变结构以不同的方式狡猾地邪恶。

答案 1 :(得分:18)

当然,这样做也不错。

struct Example {
  public readonly string Field1;
}

readonly不是必需的,但优良做法是使struct不可变。

答案 2 :(得分:4)

是的,这是可能的,是的,这通常是一种不好的做法。

如果你看看.NET框架本身,你会发现几乎所有结构都包含原始值类型。

答案 3 :(得分:4)

你不能拥有可变结构的原因是因为引用类型的行为。阅读这篇文章:http://www.yoda.arachsys.com/csharp/parameters.html

如果你有一个包含Object的结构(任何不是像int或double这样的原语)并且你复制了一个struct的实例,那么内部的Object不会被“深度”复制,因为它只是一个引用(指针)到包含实际类的内存位置。因此,如果您复制包含类实例的可变结构,则该副本将引用与原始实例相同的实例(因此上面更改了条形列表)。

如果你必须让结构变得可变,那么在readonly中创建任何类实例,或者 - 这是不好的做法 - 尽量确保你永远不要复制结构。

答案 4 :(得分:2)

是的,他们可以。

取决于。

许多人认为结构应该是不可变的,在这种情况下,持有对象的引用可能意味着它不是。

但这取决于具体情况。

答案 5 :(得分:1)

由于这是一个downvote,我试图重写一点,看看它是否会变得更清晰。这个问题很古老;但好!我最近还发现了一些详细说明这一点的链接。

我试图添加的一点是,如果你声明引用字段,你必须能够推理自己块的外部:某人使用你的结构。我添加的具体要点实际上只是声明一个struct的只读字段;但在这种情况下,结构中的字段可以改变其结果;并且很难推理。

我遇到了这个链接,程序员声明了一个包含readonly struct字段的。他班上的字段struct ---它是LinkedList<T>.Enumerator - 它因为字段为readonly而崩溃了 - 他的自己的类方法获取枚举器结构的副本,并且复制状态而不是动态。

但是,如果您继续修改代码,只需从struct字段中移除readonly工作); 然后,如果您决定将自己的类设为struct,现在再次您的结构的使用者不能将它用作只读字段,否则它们会被同样的问题所困扰。 (如果这似乎是设计的,因为你没有一个只读的枚举器,你实际上可能如果它支持重置!)

因此,如果它不是最明显的例子,我要说的是你可以推断自己的实现,但如果你是一个结构,你需要 消费者的理由是复制你的价值以及他们会得到什么。

我发现的例子链接如下。

他的类不是struct,而是包含m_Enumerator字段(据说程序员知道 struct)。

事实证明,这个类的方法获得了该值的副本,并且不起作用。 ---你可以非常仔细地检查这个块来理解它。

可以通过使字段 readonly来修复它 - 这已经指向混乱。但您可以 通过将该字段声明为interface类型--- IEnumerator<int>来修复它。

但是,如果你修复它,将该字段保留为struct,并声明它不是readonly然后选择将您的类定义为struct,然后现在,如果有人将您的 struct的实例声明为{{ 1}}某个类中的字段,再次它们会丢失!

E.G:

readonly

如果您声明一个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(); } } 字段struct,那么您就不会知道那里有什么,但是当您只是引用它时,您实际上可以更多地了解您得到的内容!这非常有趣;但只是因为语言允许interface这么多自由:你需要从非常简单的事情开始;并且只添加你可以具体推理的内容!

还有一点是,该引用还表示将默认值定义为合理;参考字段可能!如果您无意中调用了默认构造函数---始终可以使用struct ---那么您将得到一个空引用。

最后一点也是。许多人捍卫可变结构和大型可变结构。但是如果你同意,你通常会发现他们只是以一种允许他们有限地推理行为的方式确定这些对象的范围,并且结构不会泄漏到那些不变量可以改变的范围内。

......太多人开始将结构解释为&#34;就像一个类但...... x,y,z,1,2,alpha,beta disco&#34;。它必须被解释为几个只读值;期;除非您现在知道某些事情,否则您可以开始推理添加内容!

我来到accros的例子就在这里:

https://www.red-gate.com/simple-talk/blogs/why-enumerator-structs-are-a-really-bad-idea/