C# 你能在结构中有一个类吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/68640/
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-03 11:05:42  来源:igfitidea点击:

Can you have a class in a struct?

提问by Mark T

Is it possible in C# to have a Struct with a member variable which is a Class type? If so, where does the information get stored, on the Stack, the Heap, or both?

在 C# 中是否有可能有一个带有类类型成员变量的结构?如果是这样,信息存储在哪里,在堆栈上,堆上,还是两者兼而有之?

采纳答案by Eric Z Beard

Yes, you can. The pointer to the class member variable is stored on the stackwith the rest of the struct's values, and the class instance's data is stored on the heap.

是的你可以。指向类成员变量的指针与结构体的其余值一起存储在堆栈中,而类实例的数据存储在堆中。

Structs can also contain class definitions as members (inner classes).

结构还可以包含类定义作为成员(内部类)。

Here's some really useless code that at least compiles and runs to show that it's possible:

这是一些真正无用的代码,至少可以编译并运行以表明它是可能的:

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            MyStr m = new MyStr();
            m.Foo();

            MyStr.MyStrInner mi = new MyStr.MyStrInner();
            mi.Bar();

            Console.ReadLine();
        }
    }

    public class Myclass
    {
        public int a;
    }

    struct MyStr
    {
        Myclass mc;

        public void Foo()
        {
            mc = new Myclass();
            mc.a = 1;
        }

        public class MyStrInner
        {
            string x = "abc";

            public string Bar()
            {
                return x;
            }
        }
    }
}

回答by Craig Eddy

It's probably not a recommended practice to do so: see http://msdn.microsoft.com/en-us/library/ms229017(VS.85).aspx

这样做可能不是推荐的做法:请参阅http://msdn.microsoft.com/en-us/library/ms229017(VS.85).aspx

Reference types are allocated on the heap, and memory management is handled by the garbage collector.

Value types are allocated on the stack or inline and are deallocated when they go out of scope.

In general, value types are cheaper to allocate and deallocate. However, if they are used in scenarios that require a significant amount of boxing and unboxing, they perform poorly as compared to reference types.

引用类型在堆上分配,内存管理由垃圾收集器处理。

值类型在堆栈或内联上分配,并在超出范围时被释放。

一般来说,分配和释放值类型的成本更低。但是,如果它们用于需要大量装箱和拆箱的场景,与引用类型相比,它们的性能很差。

回答by Ben Voigt

The class content gets stored on the heap.

类内容存储在堆上。

A reference to the class (which is almost the same as a pointer) gets stored with the struct content. Where the struct content is stored depends on whether it's a local variable, method parameter, or member of a class, and whether it's been boxed or captured by a closure.

对类的引用(几乎与指针相同)与结构内容一起存储。struct 内容的存储位置取决于它是局部变量、方法参数还是类的成员,以及它是否被装箱或被闭包捕获。

回答by supercat

If one of the fields of a struct is a class type, that field will either hold the identityof a class object or else a null referece. If the class object in question is immutable (e.g. string), storing its identity will effectively also store its contents. If the class object in question is mutable, however, storing the identity will be an effective means of storing the contents if and only if the reference will never fall into the hands of any code which might mutate it once it is stored in the field.

如果结构的字段之一是类类型,则该字段将保存类对象的标识或空引用。如果所讨论的类对象是不可变的(例如string),则存储其身份也将有效地存储其内容。但是,如果所讨论的类对象是可变的,则当且仅当引用永远不会落入任何代码的手中时,存储标识将是存储内容的有效手段,这些代码一旦将其存储在 field 中就可能发生变异

Generally, one should avoid storing mutable class types within a structure unless one of two situations applies:

通常,除非适用以下两种情况之一,否则应避免在结构中存储可变类类型:

  1. What one is interested in is, in fact, the identity of the class object rather than its content. For example, one might define a `FormerControlBounds` structure which holds fields of type `Control` and `Rectangle`, and represents the `Bounds` that control had at some moment in time, for the purpose of being able to later restore the control to its earlier position. The purpose of the `Control` field would not be to hold a copy of the control's state, but rather to identify the control whose position should be restored. Generally the struct should avoid accessing any mutable members of the object to which it holds a reference, except in cases where it is clear that such access is referring to the current mutable state of the object in question (e.g. in a `CaptureControlPosition` or `RestoreControlToCapturedPosition` method, or a `ControlHasMoved` property).
  2. The field is `private`, the only methods which read it do so for the purpose of examining its properties without exposing the object itself it to outside code, and the only methods which write it will create a new object, perform all of the mutations that are ever going to happen to it, and then store a reference to that object. One could, for example, design a `struct` which behaved much like an array, but with value semantics, by having the struct hold an array in a private field, and by having every attempt to write the array create a new array with data from the old one, modify the new array, and store the modified array to that field. Note that even though the array itself would be a mutable type, every array instance that would ever be stored in the field would be effectively immutable, since it would never be accessible by any code that might mutate it.
  1. 事实上,人们感兴趣的是类对象的身份而不是其内容。例如,可以定义一个 `FormerControlBounds` 结构,其中包含类型为 `Control` 和 `Rectangle` 的字段,并表示控件在某个时刻具有的 `Bounds`,以便以后能够恢复控件到它之前的位置。`Control` 字段的目的不是保存控件状态的副本,而是标识应该恢复其位置的控件。通常,该结构应该避免访问它持有引用的对象的任何可变成员,除非这种访问显然是指相关对象的当前可变状态(例如在“CaptureControlPosition”或“ RestoreControlToCapturedPosition` 方法,
  2. 该字段是“private”,读取它的唯一方法是为了检查其属性而不将对象本身暴露给外部代码,并且唯一写入它的方法将创建一个新对象,执行所有更改它将永远发生在它身上,然后存储对该对象的引用。例如,可以设计一个“struct”,它的行为很像一个数组,但具有值语义,通过让结构在私有字段中保存一个数组,并通过每次尝试写入该数组来创建一个包含数据的新数组从旧数组修改新数组,并将修改后的数组存储到该字段。请注意,即使数组本身是可变类型,但存储在该字段中的每个数组实例实际上都是不可变的,

Note that scenario #1 is pretty common with generic types; for example, it's very common to have a dictionary whose "values" are the identities of mutable objects; enumerating that dictionary will return instances of KeyValuePairwhose Valuefield holds that mutable type.

请注意,场景#1 对于泛型类型很常见;例如,有一个字典的“值”是可变对象的身份是很常见的;枚举该字典将返回KeyValuePairValue字段包含该可变类型的实例。

Scenario #2 is less common. There is alas no way to tell the compiler that struct methods other than property setters will modify a struct and their use should thus be forbidden in read-only contexts; one could have a struct that behaved like a List<T>, but with value semantics, and included an Addmethod, but an attempt to call Addon a read-only struct instance would generate bogus code rather than a compiler error. Further, mutating methods and property setters on such structs will generally perform rather poorly. Such structs can be useful are when they exist as an immutable wrapper on an otherwise-mutable class; if such a struct is never boxed, performance will often be better than a class. If boxed exactly once (e.g. by being cast to an interface type), performance will generally be comparable to a class. If boxed repeatedly, performance can be much worse than a class.

场景#2 不太常见。可惜没有办法告诉编译器除了属性设置器之外的结构体方法会修改结构体,因此在只读上下文中应该禁止它们的使用;可以有一个结构类似于 a List<T>,但具有值语义,并包含一个Add方法,但尝试调用Add在只读结构实例上会生成虚假代码而不是编译器错误。此外,这种结构上的变异方法和属性设置器通常会表现得很差。当此类结构作为其他可变类的不可变包装器存在时,它们会很有用;如果这样的结构从不装箱,则性能通常会比类好。如果只装箱一次(例如通过转换为接口类型),性能通常与类相当。如果重复装箱,性能可能比类差很多。