我知道b / w值类型和引用类型的区别。但为什么我们需要两者?我们可以使用引用类型而不是值类型。
答案 0 :(得分:1)
实际上,您可以构建一种只知道引用类型的语言。价值类型主要是出于效率原因。实现数值类型和bool类型更有效,例如,作为值类型。
答案 1 :(得分:1)
值类型直接存储value
。
例如:
//I and J are both of type int
I = 20;
J = I;
这里,int
是一个值类型,这意味着上述语句将在内存中产生两个位置。对于值类型的每个实例,分配单独的内存并将其存储在堆栈中。它提供快速访问,因为值位于堆栈上。
参考类型将reference
存储到值中。
例如:
Vector X, Y; //Object is defined. (No memory is allocated.)
X = new Vector(); //Memory is allocated to Object. //(new is responsible for allocating memory.)
X.value = 30; //Initialising value field in a vector class.
Y = X; //Both X and Y points to same memory location. //No memory is created for Y.
Console.writeline(Y.value); //displays 30, as both points to same memory
Y.value = 50;
Console.writeline(X.value); //displays 50.
注意:如果变量是引用,则可以通过将其值设置为null来指示它不引用任何对象。
引用类型存储在Heap上,它提供相对较慢的访问权限,因为值位于堆上。
答案 2 :(得分:1)
要理解值类型,首先必须了解存储位置和引用类型的概念。
对象通常使用字段来表示其状态,而运行代码通常使用变量和参数来表示其状态。这些东西以及阵列插槽统称为"存储位置"。
引用类型的存储位置包含一个对象标识符,或者一个特殊的&#34; null&#34;值。这样的存储位置实际上并不包含对象 - 只是可以用来快速定位它们的标识符。声明为接口类型的存储位置(例如ICollection<T>
)包含对已知实现指定接口的对象的引用(或者&#34; null&#34;)。
复制引用类型存储位置只会复制标识符。它对被引物体的物理状态没有任何影响。从概念上讲,这似乎很简单,但它可能很棘手。问题在于,虽然引用类型存储位置在物理上保存了对象标识符,但语义上这样的存储位置可用于捕获对象的以下任何方面:
当然,物理上,引用类型存储位置保存对象的标识。尽管如此,这些位置通常在语义上用于保存其他信息。例如,考虑一个对象George
包含thing
类型的字段ICollection<integer>
。这样的存储位置将保持对整数集合的引用,这将允许它们被枚举,并且可以允许或不允许添加或移除事物。 George
的内容如何影响thing
的内容?它可以通过多种方式影响国家。
请注意,在上述所有四种情况中,存储位置的类型完全相同。关于存储位置声明的任何内容都不会表明它的哪些方面很重要。进一步注意,很多复杂性源于这样一个事实,即类引用通常会封装可变状态和身份。如果存储位置没有封装身份概念,事情就会简单得多。
输入值类型。如果有一个存储位置,其类型是值类型结构,如:
struct Point3d { public double X,Y,Z; Point3d(double X, double Y, double Z) { this.X = X; this.Y = Y; this.Z = Z; } }
该存储位置将保存该结构字段的内容(在本例中为字段double
,X
和{{1}中的三个Y
值如果一个类有一个该类型的字段,那个字段所代表的状态将是其中保存的三个数字。与类对象不同,其中等价可能依赖于内容,也可能不依赖于内容,结构类型没有这样的歧义。字段结构(有时称为PODS - 普通旧数据结构)如果它们属于同一类型,并且它们的相应字段是等效的,则它们是等效的。
尽管类可以做许多值类型不能做的事情,但这种能力会造成混乱,模糊和复杂。例如,如果有人希望拍摄快照&#34;在阶级状态中,必须知道其领域的哪些方面对其状态很重要。如果类对象Z
的状态取决于它拥有引用的对象Larry
的可变状态,那么拍摄Mike
状态的快照将还需要拍摄Larry
状态的快照(并在原始Mike
所持有的副本Larry
的所有字段或字段中存储对该快照的引用对Larry
)的引用。相比之下,复制一个外露字段结构很容易。只需复制其所有字段。
除了一些限制之外,外露场结构可以成为出色的数据持有者:
假设一个类的状态取决于5,000个3-d点。如果Mike
类型将是一个类,则必须使Point3d
类型为不可变类,或者保留现有的5,000个实例的唯一引用。如果Point3d
类型是不可变的,那么无论何时希望改变由此指示的状态的任何方面,都必须创建一个全新的Point3d
实例并放弃旧实例。如果类型是可变的,那么无论何时希望将坐标从一个点暴露给外部代码,都必须复制Point3d
,X
和Y
值。这两种选择都不太令人愉快。
使Z
暴露字段结构消除了这两个问题。由于Point3d
只不过是Point3d
,X
和Y
,因此5,000个Z
个对象的数组将只包含15,000个数字。将任何Point3d
暴露给外界将自动复制三个相关联的号码。如果有人希望改变,例如Point3d
的{{1}}坐标不会打扰其他坐标,没问题 - 如果Z
存储在数组中,可以将<9.}添加到数组插槽4的Z坐标< / p>
MyArray[4].Z += 9.8;
如果Point
存储在其他类型的集合中,事情会更加尴尬,但也不会太糟糕:
Point3d temp = MyArray[4]; temp.Z += 9.8; MyArray[4] = temp;
比Point
是一个班级更方便。
答案 3 :(得分:0)
好吧,也许您错过的一个差异是堆栈中的值类型和堆中的引用类型。这会产生很大的性能差异(因为一个是间接访问的指针,另一个是值本身)。
修改强> 我提到堆和堆栈是错误的,如评论中所示。正确的说法应该是Eric Lippert所说here:
“在桌面CLR上的C#的Microsoft实现中,当值是局部变量或临时值时,值类型存储在堆栈中,而不是lambda或匿名方法的封闭局部变量,并且方法体不是迭代器块,并且抖动选择不注册该值。“
答案 4 :(得分:0)
我认为解释它的最简单方法是:
值类型表示不变的值。无论哪个对象1
始终为1
,3.14159265359
始终为3.14159265359
。并且8/18/2012 17:23 UTC
表示始终相同的确切时刻。因此,我可以使用这些值创建一千个不同的int
或DateTime
个对象,无论它们在何处以及如何使用它们,它们总是完全相同。
但是,即使规格相同,参考类型也不总是相同。我可以在一个给定的地址建一个房子,然后把计划交给一个朋友,他可以建造另一个房子,规格相同,大小相同,甚至相同的景观和相同大小的院子,但无论你怎么努力制作它们看起来一样,两栋房子仍然永远不会完全一样。