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

在C#中是否可以使用具有类型类型的成员变量的Struct? 如果是这样,信息在哪里存储,堆栈,堆,或两者?

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

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

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

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

类内容存储在堆上。

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

如果结构的某个字段是类类型,则该字段将保留类对象的标识 ,或者保留null引用。 如果所讨论的类对象是不可变的(例如string ),则存储其标识也将有效地存储其内容。 但是,如果所讨论的类对象是可变的,那么存储标识将是存储内容的有效方法, 当且仅当引用永远不会落入任何代码的手中,一旦代码存储在字段中就可能使其变异

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

  1. 事实上,人们感兴趣的是类对象的身份而不是其内容。 例如,可以定义一个`FormerControlBounds`结构,它保存类型为`Control`和`Rectangle`的字段,并表示控制在某个时刻具有的’Bounds`,以便以后能够恢复控件到它早先的位置。 “控制”字段的目的不是保存控件状态的副本,而是识别应该恢复其位置的控件。 通常,struct应该避免访问它拥有引用的对象的任何可变成员,除非很明显这样的访问是指有关对象的当前可变状态(例如在`CaptureControlPosition`或` RestoreControlToCapturedPosition`方法,或`ControlHasMoved`属性)。
  2. 该字段是`private`,唯一读取它的方法是为了检查其属性而不将对象本身暴露给外部代码,并且编写它的唯一方法将创建一个新对象,执行所有突变它将永远发生在它上面,然后存储对该对象的引用。 例如,人们可以设计一个`struct`,它的行为很像一个数组,但具有值语义,通过让struct在私有字段中保存一个数组,并且每次尝试编写数组都会创建一个包含数据的新数组从旧的数组中,修改新数组,并将修改后的数组存储到该字段。 请注意,即使数组本身是一个可变类型,每个存储在字段中的数组实例都是有效的不可变的,因为任何可能会使其变异的代码都无法访问它。

请注意,场景#1在generics类型中非常常见; 例如,拥有一个字典,其“值”是可变对象的身份是很常见的; 枚举该字典将返回KeyValuePair的实例,其Value字段包含该可变类型。

场景#2不太常见。 没有办法告诉编译器除了属性设置器之外的结构方法将修改结构,因此它们的使用应该在只读上下文中被禁止; 一个可以有一个行为类似List ,但具有值语义,并包含一个Add方法,但尝试在只读结构实例上调用Add会产生伪代码而不是编译器错误。 此外,这种结构上的变异方法和属性设定器通常表现得相当差。 当这些结构作为一个不可变的包装器存在于其他可变类时,它们可能是有用的; 如果这样的结构永远不会被装箱,那么性能往往比一个类好。 如果只装箱一次(例如通过投射到界面类型),性能通常与一个类相当。 如果重复加框,性能可能比一类差。

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

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

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

通常,值类型分配和取消分配更便宜。 但是,如果它们在需要大量装箱和拆箱的场景中使用,则与参考类型相比,它们表现不佳。