我对struct型有些了解。但我不明白:这是为了什么?我什么时候用的?类、简单值类型和枚举——这就是我所需要的。
有什么建议吗?
UPD:请!不要告诉我结构在堆栈中(我知道这个:)。什么结构?
- 不是您问题的答案,但如果您使用结构,请避免使用可变结构:stackoverflow.com/questions/441309/why-are-mutable-structs-e‌&8203;vil
- 答案是它们是存储在堆栈中的值类型,而不是存储在堆栈中的引用类型。所以答案只是你说你不想要的答案。
- 如果你知道"它在堆栈中",那么你应该知道它们的用途。
- 斯坦,一点也不。
- @斯坦:你一文不值。:)
- 我的评论是讽刺的,暗示说,仅仅因为他知道"它在堆栈中",他应该了解这真正意味着什么,但是你的评论真的很有趣。
- +1:大多数是一个好问题,因为提交的答案中至少有一半有多个反对票;)
- 斯坦·R,再想一想,你说得对。堆栈存储是对值类型的默认实现,这是有原因的。不过,这是一个足够令人困惑的话题。OP可以使用一些帮助。
- 注:blogs.msdn.com/ericlippet/archive/2009/04/27/…blogs.msdn.com/ericlippet/archive/2009/05/04/…
- dupe:stackoverflow.com/questions/13049/…
如果需要值类型语义,则选择结构。如果需要引用类型语义,可以选择一个类。所有其他问题都是这个问题的次要问题。
- 这是绝对正确的(正如您在其他地方的卓越评论),但可能没有帮助:我认为大多数会问这个问题的人显然不理解引用类型和值类型语义之间的区别。
- 但问题是"结构-它是为了什么?"不是"值类型和引用类型之间的区别是什么"。如果海报想要知道这一点,那么他应该在这里评论一下,或者更好地,打开一个新问题。我相信很多人也会乐意回答这个问题。
- +1个很好的答案。简洁、绝对正确。
- 当然,这个答案是正确的,但是如果你解释这些差异的话,会更好的。其中有一些差异。
- @斯坦·R:正如我在对杰夫·斯泰纳尔的回答中提到的,我认为最好在一个单独的问题中解决这个问题。结构和类分别是值类型和引用类型语义类型的实现;它们不定义概念。
- @杰森。我同意杰森的观点,但我们不能真的把每一个问题都弄错。如果我们这样做了,就永远不会有答案。虽然有很多问题可以回答这个确切的问题,但我不知道为什么这个问题还没有结束。
msdn提供了一个指南:在类和结构之间进行选择:
Consider defining a structure instead of a class if instances of the type are small and commonly short-lived or are commonly embedded in other objects.
Do not define a structure unless the type has all of the following characteristics:
-
It logically represents a single value, similar to primitive types (integer, double, > and so on).
-
It has an instance size smaller than 16 bytes.
-
It is immutable.
-
It will not have to be boxed frequently.
- 我讨厌这个指南,因为它谈论的是性能问题和结构/堆栈类/堆实现细节的胡说八道。这些关注点与主要关注点(语义)相距甚远。
- @杰森,我同意。
- @杰森:实体有三种类型:可变值类型、可变引用类型和不可变类型。唯一语义上不同于引用类型的结构是可变结构。我个人认为可变值类型有时是有用的,并且认为大多数关于它们的抱怨实际上是对.NET如何处理它们的局限性的抱怨(例如,不能指定自定义装箱运算符),但是有些人讨厌它们。
应该是结构的事物(因为它们是值):
- struct Color
- struct Point
- struct Rectangle
- struct GLVertex(包含位置、颜色、普通和texcoord)
- struct DateTime
应该是类的东西(因为它们是您所指的东西):
- class RandomGenerator
- 7
- class Thread
- class Window
为什么?考虑下面的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Button
{
public Point Location { get; set; }
}
class Program
{
public static void Main()
{
var button = Util.GetButtonFromSomewhere();
var location = button.Location;
Util.DrawText("one", location);
location.Y += 50;
Util.DrawText("two", location);
location.Y += 50;
Util.DrawText("three", location);
}
} |
这将绘制3个垂直对齐的文本标签。但如果point是一个类,这也会移动按钮,这真是出乎意料:var location = button.Location感觉应该复制一个值,而不是引用!换句话说,我们认为点是值类型,而不是引用类型。"这里的"值"是用在数学意义上的值。考虑数字5,它是一个抽象的对象,"在外面的某个地方",你只是"指"它。同样,一个简单的点就是。它不在任何地方,我们不能改变它。因此,我们选择将其作为一个结构,因此它具有用户期望的语义。
另一方面,我们可以有class Button { public Window Parent { get; set; } }。这里,Parent是一个实体,所以我们用一个引用类型来表示它—Window。使用像myButton.Parent.Redraw();这样的代码可能是有意义的。所以Window应该是一个类。
到现在为止,一直都还不错。但这一切对你来说可能太模糊了。你如何真正决定某个"感觉"的东西是参考还是价值?我的经验法则很简单:
Foo a = b; a.Mutate();该怎么办?
- 如果它看起来应该保持b不变,则使foo成为一个结构。
- 否则就把它变成一个类。
这里使用最小惊喜原则。
- 回答"struct vs class"问题的最佳方法通常是问你的"what should…"问题,尽管我会用"a.x=5"替换"a.mutate()";我也会注意到,vb.net和c有一些恼人的限制,即使某些东西应该具有值语义(例如,几乎不可能实现,因为它需要一个人使用类)。使包含数组的结构的行为类似于值类型)。
首先,您必须了解值类型和引用类型之间的区别。我想既然你说跳过那部分,你就会明白它是什么。
struct是一个值类型,您将获得处理值类型的所有特权。
结构按值传递。当你做类似的事情时
datetime time=new datetime();datetime newtime=时间;//您没有引用时间//而是创建了一个新实例
结构不是轻量级类,它们可能有许多方法,只需看看DateTime结构。
结构在性能上可能更轻,但并非一直如此。考虑将大型结构传递给方法。因为每次向方法传递一个结构时,结构都是值类型,所以会创建结构的新实例,因此每次都要复制该结构。如果您有一个相当大的结构,这将是一个更大的性能冲击。
由于结构是值类型,因此有时可能需要对其进行装箱和取消装箱。
简而言之,使用结构表示内存中的原子值。
- 如果投反对票的人解释投反对票是为了什么,那会更好吗?
- 结构不是不变的(但我没有投反对票)。请参阅stackoverflow.com/questions/608542/immutatability-of-structs
- @布瑞恩。谢谢,我宁愿有人向我指出这一点,而不是投反对票。我所说的不可变是指保存结构的变量是不可变的。显然,一个结构一旦创建就不是只读的,当然,根据定义,它是可变的,除非您创建了一个不变的结构(我认为这是一个很好的实践)。
简单值类型最好通过结构实现。
Struct Usage Guidelines
It is recommended that you use a
struct for types that meet any of the
following criteria:
1 2 3 4
| * Act like primitive types.
* Have an instance size under 16 bytes.
* Are immutable.
* Value semantics are desirable. |
您还必须了解在堆上分配了类实例。结构-是Vallue类型-并在堆栈上分配。
- 好啊。但是,如果使用struct的原因仅仅是内存优化,那么…你在使用反射吗?大量的SQL查询?林克?我认为,这些东西中的任何一个都与struct重叠在一起。
- 结构不总是在堆栈上分配。选择结构的主要原因与在堆栈上分配的实现细节无关。
- 对你给出的解释稍加思考:blogs.msdn.com/ericlippert/archive/2009/04/27/…
- 我不是说结构只对MEM优化有用。结构是创建值类型的理想工具,这就是我要说的。杰森:在.NET中,如何创建在堆上分配的结构?
- 弗雷德里克,阅读上面链接的埃里克·利珀特的解释。
- @Frederik Gheysels:装箱结构、类的成员结构以及由于匿名方法或迭代器块中的外部变量而提升的结构都分配在堆上。
- @佩德罗:很抱歉,但你应该更好地阅读我的帖子,并且重新阅读埃里克·利珀特的文章。在那篇文章中,Eric说"值类型并不总是在堆栈上分配的"。这是正确的,因为您也可以使用类(例如System.String)实现值类型。但是,我说过在堆栈上分配了一个结构,并且该结构是一个值类型。我没有说在堆栈上分配了"value"类型。就像我说的:"一只小鸟,一只小鸟不能飞",然后你就得出结论,我说了"小鸟不能飞"。
结构用于表示某个对象,该对象的标识由存储在其属性中的值定义,而不是由ID或键定义。它们被称为"值类型",而不是被称为"实体类型"的tyo对象,其标识随着时间的推移而保持,并且不依赖于对象属性的值。
例如,一个人类(一个实体)的身份在一个会话到另一个会话之间持续存在,甚至在一年到一年之间,尽管该人的地址、电话号码、雇主等可能会从一个实例更改为另一个实例。如果您无意中在内存中同时有两个代表同一个人/实体的Person类实例,那么它们的属性值相同是很重要的。
calendarMonth对象otah(值类型)只有由指定它是哪个日历月的值定义的标识…不管您可能使用了多少"2009年3月"实例,它们都是可互换的和同等的。另一个例子可能是表示税务计划中财务分析指定的对象。地址对象就是一个很好的例子。(这里不讨论与个人、企业或任何其他实体的地址关联,只讨论地址本身)。更改地址的任何属性都会使其成为不同的地址。一旦您在内存中创建了一个地址对象,它就与其他具有相同属性的地址对象具有同等性和可互换性。这就是为什么这些值类型通常是不可变的。当你需要一个属性值时,用你需要的属性值创建一个新的属性值,当你使用完它后,扔掉它。
当需要具有值(而不是引用)语义的"类"时,可以使用结构。
- 请举例?
- 假设您想要一个复杂类型来存储复数。复杂类型的行为应该与int完全相同,因为两者都是一些类型的数字。不能将点声明为类,因为类是引用类型。表达式"complex a=b"应指定"a"值b,而不是引用它。
示例:假设您希望一个数据类型在x y z处表示坐标。您不需要任何功能,只需要三个变量。一个结构可以很好地解决这个问题,一个类可能会被过度杀戮。
- 感谢您这样做:)但是,现在我认为,这样的优化是无用的:在使用反射和大型SQL查询时,谁真的关心这么小的事情(ORM的3个联接,哈哈:)我只看到一件有用的事情——struct不能为空。
- 为工作选择合适的工具也是一个问题。当我看到一个结构时,我马上就知道从中期望什么。对于像我的例子这样的东西,一个简单的结构感觉更正确。
如果你不知道为什么你需要它,你可能不会。
结构是值类型而不是引用类型。如果你不知道这意味着什么,那么你可能不需要它。
- 但我可能错过了什么吗?我可以不使用枚举进行编码,但我想这样做不太舒服。
- stuct和enum不是一回事。你为什么要提到他们?
- @莫名其妙的人,他用枚举作为逻辑的反例。仅仅因为他不知道结构是用来做什么的并不意味着知识对他没有用处。同样地,他也可以在不理解Enums的情况下通过,但这种知识会使他的生活更容易。
- 我没有说知识是没有用的,我的观点是,他可能不在一个理解的层次上,在这个层次上,结构和类之间的区别对他的决定很重要。我在编程中很少需要结构,即使知道它们的不同。
- 我很尊敬,但我强烈反对。如果您不知道结构和枚举之间的关系,那么可能您"不"也需要使用…很明显,您不知道何时何地以及如何使用结构,或者您不会对它们如此消极。
- @查尔斯布雷塔纳-我想你误解了。我对结构或枚举一点也不消极。也许你应该重读。
事实上,我认为struct是C的遗产,我不认为我们必须在任何情况下使用它。也许有时候你觉得把东西放在堆栈上而不是堆上会更有效率;但是当Java/C从不把效率作为它的第一站,所以忽略它:这是我的观点。