C#编程语言(七):值类型与引用类型
值類型與引用類型
值類型:派生自System.ValueType類的類型是值類型,派生自ValueType的類型都會自動在棧(stack)上進行分配,因此有一個可預測的生命周期,而且非常高效。
引用類型:在繼承鏈上沒有System.ValueType的類型(如System.Type、System.String、System.Array、System.Exception以及System.Delegate)不會在棧上分配,而是在垃圾回收堆(heap)上進行分配。
另:C#中的所有基類型都是結構類型(例如:int對應System.Int32結構),結構類型是值類型;類類型是引用類型;棧的執行效率要比堆的執行效率高,可是棧的資源有限,不適合處理大的邏輯復雜的對象。所以結構處理作為基類型對待的小對象,而類處理某個商業邏輯;因為結構是值類型所以結構之間的賦值可以創建新的結構,而類是引用類型,類之間的賦值只是復制引用;(所以在以結構為參數傳遞時,最好使用ref,這樣只傳遞地址引用,能夠提高效率,同時也應注意這樣結構的值也會隨著方法調用而改變)
從功能上說,ValueType的唯一目的是"重寫"由Object定義的虛方法來使用基于值而不是基于引用的語法:
public abstract class ValueType:object{
protected ValueType();
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
}
由于值類型是基于值語法的,結構(包括所有數值數據類型如Int32等、自定義結構、枚舉)的生命周期都是可預測的。但結構離開定義的作用域時,就會立即從內存中清除。
static void LocalValueType(){
int i=0;//int是System.Int32結構
Point p=new Point();//Point是自定義結構
}?//出了方法作用域,i與p被清除了(已經被彈出棧)。
值類型與類類型的賦值
當把值類型賦值給另外一個時,是對字段成員逐一進行復制。對于System.Int32這樣簡單的數據類型,唯一需要復制的成員就是數值。而對像Point復雜點的自定義結構,就需要把Point所有的字段復制到新的結構變量中。改變新的結構變量的字段的值不會影響原結構變量的字段值。
當對引用類型進行復制時,是把原引用變量的地址指向賦給新的引用變量,兩個引用變量指向同一個對象。
View Code //結構Point的定義struct Point
{
public int X;
public int Y;
public Point(int XPos, int YPos)
{
X = XPos;
Y = YPos;
}
public void Increment()
{
X++; Y++;
}
public void Decrement()
{
X--; Y--;
}
public void Display()
{
Console.WriteLine("X = {0}, Y = {1}", X, Y);
}
}
//PointRef類型的定義
class PointRef
{
public int X;
public int Y;
public PointRef(int XPos, int YPos)
{
X = XPos;
Y = YPos;
}
public void Increment()
{
X++; Y++;
}
public void Decrement()
{
X--; Y--;
}
public void Display()
{
Console.WriteLine("X = {0}, Y = {1}", X, Y);
}
}
static void Main(string[] args)
{
Console.WriteLine("***** 值類型 / 引用類型 *****\n");
ValueTypeAssignment();
ReferenceTypeAssignment();
Console.ReadLine();
}
static void ValueTypeAssignment()
{
Console.WriteLine("指定值類型\n");
Point p1 = new Point(10, 10);
Point p2 = p1;
p1.Display();
p2.Display();
p1.X = 100;
Console.WriteLine("\n=> 改變 p1.X\n");
p1.Display();
p2.Display();
}
static void ReferenceTypeAssignment()
{
Console.WriteLine("指定引用類型\n");
PointRef p1 = new PointRef(10, 10);
PointRef p2 = p1;
p1.Display();
p2.Display();
p1.X = 100;
Console.WriteLine("\n=> 改變 p1.X\n");
p1.Display();
p2.Display();
}
包含引用類型的值類型
默認情況下,但值類型包含其他引用類型時,賦值將產生一個引用副本。這樣就有兩個獨立的結構,每個都包含指向內存中同一個對象的引用(也就是淺復制)。當先執行深度賦值(將引用的對象的狀態完全復制到新的對象中)時,引用類型就需要實現ICloneable結構。
View Code class ShapeInfo{
public string infoString;
public ShapeInfo(string info)
{ infoString = info; }
}
struct Rectangle
{
public ShapeInfo rectInfo;
public int rectTop, rectLeft, rectBottom, rectRight;
public Rectangle(string info, int top, int left, int bottom, int right)
{
rectInfo = new ShapeInfo(info);
rectTop = top; rectBottom = bottom;
rectLeft = left; rectRight = right;
}
public void Display()
{
Console.WriteLine("String = {0}, Top = {1}, Bottom = {2}, Left = {3}, Right = {4}",
rectInfo.infoString, rectTop, rectBottom, rectLeft, rectRight);
}
}
static void Main(string[] args)
{
Console.WriteLine("***** 值類型 / 引用類型 *****\n");
ValueTypeContainingRefType();
Console.ReadLine();
}
static void ValueTypeContainingRefType()
{
Console.WriteLine("-> 創建 r1");
Rectangle r1 = new Rectangle("初始的信息", 10, 10, 50, 50);
Console.WriteLine("-> 把r1賦給r2");
Rectangle r2 = r1;
Console.WriteLine("-> 改變r2的值");
r2.rectInfo.infoString = "新的字符信息!";
r2.rectBottom = 4444;
r1.Display();
r2.Display();
}
可以看出,當使用r2的引用改變信息字符串的值時,r1的引用顯示了同樣的值(r1的引用的值也改變了)。
按值轉遞引用類型與按引用轉遞引用類型
引用類型可以作為參數傳遞給類型成員。但是,按按值轉遞一個對象與按引用轉遞一個對象大有不同。
View Code static void Main(string[] args){
//按值傳遞
Console.WriteLine("*********按值傳遞引用類型對象*****");
Car c = new Car("寶馬", 100);
Console.WriteLine("按值傳遞前對象的狀態。");
c.Print();
SendACarByValue(c);
Console.WriteLine("按值傳遞後對象的狀態。");
c.Print();
Console.ReadLine();
//按引用傳遞
Console.WriteLine("*********按值傳遞引用類型對象*****");
Car bmw = new Car("寶馬", 100);
Console.WriteLine("按引用傳遞前對象的狀態。");
bmw.Print();
SendACarByReference(ref bmw);
Console.WriteLine("按引用傳遞後對象的狀態。");
bmw.Print();
Console.ReadLine();
}
static void SendACarByValue(Car c)
{
//改變c的Speed屬性的值
c.Speed = 150;
//給c重新賦值(賦予新的Car對象的指向)
c = new Car("保時捷", 250);
}
static void SendACarByReference(ref Car c)
{
//改變c的Speed屬性的值
c.Speed = 150;
//給c重新賦值(賦予新的Car對象的指向)
c = new Car("保時捷", 250);
}
class Car
{
public int Speed { get; set; }
public string Name { get; set; }
public Car(string n, int s)
{
this.Name = n;
this.Speed = s;
}
public Car() { }
public void Print()
{
Console.WriteLine("汽車{0}的速度是{1}", this.Name, this.Speed);
}
}
可以看出:
一、如果按值傳遞引用類型,被調用者可能改變對象的狀態數據的值,但不能改變所引用的對象(不能指向另一個新的對象,即指向沒有改變)。
二、如果按引用傳遞引用類型,被調用者可能改變對象的狀態數據的值和所引用的對象(指向都改變了)。
小結
值類型與引用類型的比較:
| 問題 | 值類型 | 引用類型 |
| 這個類型分配在哪里? | 分配在棧(stack)上 | 分配在托管堆(heap)上 |
| 變量是怎么表示的? | 是副本 | 變量指向被分配的對象所占的內存 |
| 能否作為其他類型的基類? | 不能,是封閉(sealed)的 | 能,若沒有聲明是封閉時可以。 |
| 能為這個類型定義構造函數嗎? | 能,但默認的構造函數被保留 (即自定義構造函數必須全部帶有參數) | 能 |
| 這個類型的變量什么時候消亡? | 當超出定義它們的作用域時 | 當托管堆被垃圾回收器回收時 |
轉載于:https://www.cnblogs.com/bruce-wong/archive/2011/03/01/1967107.html
總結
以上是生活随笔為你收集整理的C#编程语言(七):值类型与引用类型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新云网站管理系统最新版注入漏洞
- 下一篇: 使用文章来增加流量和排名