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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/945664/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 03:57:58  来源:igfitidea点击:

Can structs contain fields of reference types

c#structreference-type

提问by James Hay

Can structs contain fields of reference types? And if they can is this a bad practice?

结构可以包含引用类型的字段吗?如果他们可以,这是一种不好的做法吗?

采纳答案by Jon Skeet

Yes, they can. Is it a good idea? Well, that depends on the situation. Personally I rarely create my own structs in the first place... I would treat any new user-defined struct with a certain degree of scepticism. I'm not suggesting that it's alwaysthe wrong option, just that it needs more of a clear argument than a class.

是的他们可以。这是个好主意吗?嗯,这取决于情况。就我个人而言,一开始我很少创建自己的结构......我会以一定程度的怀疑态度对待任何新的用户定义结构。我并不是说它总是错误的选择,只是它需要比类更清晰的论点。

It would be a bad idea for a struct to have a reference to a mutable object though... otherwise you can have two values which lookindependent but aren't:

但是,对于一个结构体来说,引用一个可变对象是一个坏主意……否则你可以有两个看起来独立但不是的值:

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

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

Mutable structs are evil. Immutable structs with references to mutable types are sneakily evil in different ways.

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

回答by Lasse V. Karlsen

Yes they can.

是的他们可以。

It depends.

这取决于。

Many hold the stance that a struct should be immutable, and in this case, holding a reference to an object could mean it isn't.

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

But it depends on the situation.

但这取决于情况。

回答by JaredPar

Sure thing and it's not bad practice to do so.

当然,这样做也不错。

struct Example {
  public readonly string Field1;
}

The readonly is not necessary but it is good practice to make struct's immutable.

readonly 不是必需的,但使结构不可变是一种很好的做法。

回答by Judah Gabriel Himango

Yes, it is possible, and yes, it is usually a bad practice.

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

If you look at the .NET framework itself, you'll see virtually all structs contain primitive value types alone.

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

回答by Matthew

The reason you cannot have mutable structs is because of the behavtheitroad of reference types. Read this article: http://www.yoda.arachsys.com/csharp/parameters.html

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

When you have a struct that contains an Object (anything that isn't a primitive like int or double) and you copy an instance of the struct, the Object inside isn't "deep" copied, because it is simply a reference (pointer) to a memory location containing the actual class. So if you copy a mutable struct that contains class instances, the copy will be referencing the same instances as the original (Hence bar's list being changed above).

当你有一个包含对象的结构(任何不是像 int 或 double 这样的原始类型的东西)并且你复制了结构的一个实例时,里面的对象不是“深”复制的,因为它只是一个引用(指针) 到包含实际类的内存位置。因此,如果您复制包含类实例的可变结构,则该副本将引用与原始实例相同的实例(因此上面的栏列表已更改)。

If you absolutely have to have the struct be mutable, make any class instances inside readonly, or - this is the bad practice - try to ensure that you never make a copy of the struct.

如果您绝对必须让结构可变,请将任何类实例设置为只读,或者 - 这是不好的做法 - 尝试确保您永远不会复制该结构。

回答by Steven Coco

Since this got a downvote, I'm trying to rewrite a little to see if it can become clearer. This question is old; but good! I recently also came across a few links that elaborate on this.

由于这得到了否决,我试图重写一点,看看它是否可以变得更清晰。这个问题很老了;但是很好!我最近也遇到了一些详细说明这一点的链接。

The point I'm trying to add is that if you dodeclare reference fields, you have to be able to reason outsideof your own block: when someone usesyour struct. The specific point I added was really only about declaring a readonly field of a struct; but in that case, the fields you have in your struct can change their results; and it's hard to reason about.

我要补充的一点是,如果您确实声明了引用字段,则必须能够在自己的块之外进行推理:当有人使用您的结构时。我添加的具体点实际上只是关于声明结构的只读字段;但在这种情况下,结构中的字段可以更改其结果;这很难推理。

I came across this link, where the programmer declared a classcontaining a readonlystructfield. The fieldin his class is a struct--- it's a LinkedList<T>.Enumerator-- and it broke because the field is readonly--- his own class methods get a copy of the enumerator struct and the state is copied and not dynamic.

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

But, if you go ahead and fix his code by simply removing the readonlyfrom the struct field (which works); and then however, if you then decide to make your own class a struct, now once again, a consumer of yourstruct cannot use it as a readonly field or else they in turn get bitten by the same problem. (And if this seems contrived because you won't have a readonly enumerator, you actually mightif it supports Reset!)

但是,如果您继续并通过简单地readonly从结构字段(有效)中删除 来修复他的代码;然而,如果你决定创建你自己的类 a struct那么你的结构的使用者不能将它用作只读字段,否则他们反过来会被同样的问题所困扰。(如果这看起来是人为的,因为您没有只读枚举器,那么如果它支持重置,您实际上可能会这样做!)

So if it's not the clearest example, the point I'm making is that you can reason about your own implementation, but if you are a struct you need to alsoreason about consumers that copy your value and what they will get.

所以,如果它不是最明显的例子,我提出的观点是,你可以推理自己的实现,但如果你是一个结构,你需要的原因有关消费者,复制你的价值,他们会得到。

The example i found is linked below.

我找到的示例链接如下。

His class is not a struct, but does contain the m_Enumerator field (and the programmer supposedly knows that it isa struct).

他的类不是 a struct,但确实包含 m_Enumerator 字段(并且程序员应该知道它a struct)。

It turns out that this class's methods get a copy of that value, and do not work. --- You can actually examine thisblock very carefully to understand that.

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

You canfix it by making the field notreadonly--- which is already pointing at confusing. But you can alsofix it by declaring the field as the interfacetype --- IEnumerator<int>.

可以通过将字段设置为not修复它readonly--- 这已经令人困惑。但是您可以通过将字段声明为interface类型 ---来修复它IEnumerator<int>

But, if you dofix it by leaving the field declared as the structand declare it not readonly, andthen choose to define your class as a struct, then nowif someone declares an instance of yourstructas a readonlyfield in some class, againthey lose!

但是,如果你修复它将该字段声明为struct,并宣布它不是readonly随后选择定义类的struct,那么现在如果有人宣布一个实例struct作为一个readonly领域在某些类,失去!

E.G.:

例如:

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();
    }
}

If you declare a structwith an interfacefield, you won't know what you have in there, but yet you can actually reason more about what you get when you simply reference it! This is very interesting; but only because the language allows so many freedoms with a struct: you need to start from something very simple; and add only what you can concretely reason about!

如果你struct用一个interface字段声明 a ,你不会知道你在那里有什么,但是当你简单地引用它时,你实际上可以对你得到的东西进行更多的推理!这很有趣;但仅仅是因为语言允许如此多的自由struct:你需要从一些非常简单的事情开始;并只添加您可以具体推理的内容!

One more point is that the reference also says you shoulddefine the default values as reasonable; which is notpossible with a reference field! If you inadvertently invoke the default constructor --- always possible with a struct--- then you get a null reference.

还有一点是参考文献还说您应该将默认值定义为合理的;这是不是可能与基准场!如果您无意中调用了默认构造函数 --- 总是可以使用struct--- 那么您将获得一个空引用。

One last note also. Many folks defend mutable structs, and large mutable structs. But if you peer in, you usually discover that they are simply scoping those objects in a way that allows them to finitely reason about the behaviors, and the structs do not leak into scopes where those invariants couldchange.

还有最后一点。许多人捍卫可变结构和大型可变结构。但是如果你仔细观察,你通常会发现它们只是以一种允许它们对行为进行有限推理的方式来限定这些对象,并且结构不会泄漏到那些不变量可能改变的范围中。

... Too many people begin explaining structs as "Just like a class but ... x, y, z, 1, 2, alpha, beta disco". It must be explained as a couple of readonly values; period; except now that you know something, you can begin to reason about adding something!

... 太多人开始将结构解释为“就像一个类,但是 ... x, y, z, 1, 2, alpha, beta disco”。它必须解释为几个只读值;时期; 除了现在你知道了一些东西,你可以开始推理添加一些东西!

The example I came accros is here:

我来的例子在这里:

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

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