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

时间:2008-09-16 01:45:45

标签: c# class struct value-type reference-type

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

4 个答案:

答案 0 :(得分:23)

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

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

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

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

答案 1 :(得分:13)

类内容存储在堆上。

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

答案 2 :(得分:3)

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

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

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

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

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

答案 3 :(得分:2)

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

  

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

     

值类型在堆栈或内联中分配并被释放   当他们超出范围。

     

通常,值类型分配和解除分配的成本更低。   但是,如果它们用于需要重要的场景   拳击和拆箱的数量,与...相比表现不佳   参考类型。