程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> 可空類型 -- C#入門經典(第3版)

可空類型 -- C#入門經典(第3版)

編輯:.NET實例教程

首先論述另一個較簡單的泛型類型(nullable type):可空類型,解決值類型的一個小問題。

12.2.1 可空類型

在前面的章節中,介紹了值類型(大多數基本類型,例如int、double和所有的結構)區別於引用類型(string和所有的類)的一種方式:值類型必須包含一個值,它們可以在聲明之後、賦值之前,在未賦值的狀態下存在,但不能以任何方式使用。而引用類型可以是null。

有時讓值類型為空是很有用的,泛型使用System.Nullable<T>類型提供了使值類型為空的一種方式。例如:

System.Nullable<int> nullableInt;

這行代碼聲明了一個變量nullableInt,它可以擁有int變量能包含的任意值,還可以擁有值null。所以可以編寫下面的代碼:

nullableInt = null;

如果nullableInt是一個int類型的變量,上面的代碼是不能編譯的。

前面的賦值等價於:

nullableInt = new System.Nullable<int>();

與其他變量一樣,無論是初始化為null(使用上面的語法),還是通過給它賦值來初始化,都不能在初始化之前使用它。

可以像測試引用類型一樣,測試可空類型,看看它們是否為null:

if (nullableInt == null)

{

...

}

另外,可以使用HasValue屬性:

if (nullableInt.HasValue)

{

...

}

這不適用於引用類型,即使引用類型有一個HasValue屬性,也不能使用這種方法,因為引用類型的變量值為null,就表示不存在對象,當然就不能通過對象來訪問這個屬性,此時會拋出一個異常。

使用Value屬性可以查看引用類型的值。如果HasValue是true,就說明Value屬性有一個非空值。但如果HasValue是false,就說明變量被賦予了null,訪問Value屬性會拋出System. InvalidOperationException類型的異常。

可空類型要注意的一點是,它們非常有用,以致於修改了C#語法。上面可空類型的變量不使用上述語法,而是使用下面的語法:

int? nullableInt;

int ?是System.Nullable<int>的縮寫,但可讀性更高。在後面的章節中就使用這個語法。

1. 運算符和可空類型

對於簡單類型如int,可以使用+、–等運算符來處理值。而對於可空類型,這是沒有區別的:包含在可空類型中的值會隱式轉換為需要的類型,使用適當的運算符。這也適用於結構和自己提供的運算符。例如:

int? op1 = 5;

int? result = op1 * 2;

注意其中result變量的類型也是int?。下面的代碼不會編譯:

int? op1 = 5;

int result = op1 * 2;

為了使上面的代碼正常工作,必須進行顯式轉換:

int? op1 = 5;

int result = (int)op1 * 2;

只要op1有一個值,上面的代碼就可以正常運行,如果op1是null,就會生成System.Invalid OperationException類型的異常。

這就引出了下一個問題:當運算等式中的一個或兩個值是null時,例如上面代碼中的op1,會發生什麼情況?答案是:對於除了bool?之外的所有簡單可空類型,該操作的結果是null,可以把它解釋為“不能計算”。對於結構,可以定義自己的運算符來處理這種情況(詳見本章後面的內容)。對於bool?,為&和 | 定義的運算符會得到非空返回值,如表12-1所示。

表 12-1

1pt solid" valign="top" width="139">

op1

op2

op1 & op2

op1 | op2

true

true

true

true

true

false

false

true

true

null

null

-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 101.75pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid" valign="top" width="136">

true

false

true

false

true

false

false

m; BORDER-LEFT: medium none; WIDTH: 109.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid" valign="top" width="146">

false

false

false

null

false

null

null

1pt solid" valign="top" width="146">

true

null

true

null

false

false

null

d" valign="top" width="139">

null

null

null

null

 

這些運算符的結果與我們想像的一樣,如果不需要知道其中一個操作數的值,就可以計算出結果,則該操作數是否為null就不重要。

2. ??運算符

為了進一步減少處理可空類型所需的代碼量,使可空變量的處理變得更簡單,可以使用??運算符。這個運算符允許提供可空類型是null和不是null時的默認值,其用法如下:

int? op1 = null;

int result = op1 * 2 ?? 5;

在這個示例中,op1是null,所以op1*2也是null。但是,??運算符檢測到這個情況,並把值5賦予result。這裡特別要注意,在結果中放入int類型的變量result不需要顯式轉換。??運算符會自動處理這個轉換。可以把??等式的結果放在int?中:

int? result = op1 * 2 ?? 5;

在處理可空變量時,??運算符有許多用途,它也是提供默認值的一種方便方式,不需要使用if結構中的代碼塊。

在下面的示例中,將介紹可空類型Vector。

試試看:可空類型

(1) 在目錄C:\BegVCSharp\Chapter12下創建一個新控制台應用程序項目Ch12Ex01。

(2) 使用VS快捷方式,在文件Vector.cs中添加一個新類Vector。

(3) 修改Vector.cs中的代碼,如下所示:

public class Vector

{

public double? R = null;

public double? Theta = null;

 

public double? ThetaRadians

{

get

{

// Convert degrees to radians.

return (Theta * Math.PI / 180.0);

}

}

 

public Vector(double? r, double? theta)

{

// Normalize.

if (r < 0)

{

r = -r;

theta += 180;

}

theta = theta % 360;

 

// Assign fIElds.

R = r;

Theta = theta;

}

 

public static Vector Operator +(Vector op1, Vector op2)

{

try

{

// Get (x, y) coordinates for new vector.

double newX = op1.R.Value * Math.Sin(op1.ThetaRadians.Value)

+ op2.R.Value * Math.Sin(op2.ThetaRadians.Value);

double newY = op1.R.Value * Math.Cos(op1.ThetaRadians.Value)

+ op2.R.Value * Math.Cos(op2.ThetaRadians.Value);

 

// Convert to (r, theta).

double newR = Math.Sqrt(newX * newX + newY * newY);

double newTheta = Math.Atan2(newX, newY) * 180.0 / Math.PI;

 

// Return result.

return new Vector(newR, newTheta);

}

catch

{

// Return "null" vector.

return new Vector(null, null);

}

}

 

public static Vector Operator -(Vector op1)

{

return new Vector(-op1.R, op1.Theta);

}

 

public static Vector Operator -(Vector op1, Vector op2)

{

return op1 + (-op2);

}

 

public override string ToString()

{

// Get string representation of coordinates.

string rString = R.HasValue ? R.ToString() : "null";

string thetaString = Theta.HasValue ? Theta.ToString() : "null";

 

// Return (r, theta) string.

return string.Format("({0}, {1})", rString, thetaString);

}

}

(4) 修改Program.cs中的代碼,如下所示:

class Program

{

public static void Main(string[] args)

{

Vector v1 = GetVector("vector1");

Vector v2 = GetVector("vector1");

Console.WriteLine("{0} + {1} = {2}", v1, v2, v1 + v2);

Console.WriteLine("{0} - {1} = {2}", v1, v2, v1 - v2);

Console.ReadKey();

}

 

public static Vector GetVector(string name)

{

Console.WriteLine("Input {0} magnitude:", name);

double? r = GetNullableDouble();

Console.WriteLine("Input {0} angle (in degrees):", name);

double? theta = GetNullableDouble();

return new Vector(r, theta);

}

 

public static double? GetNullableDouble()

{

double? result;

string userInput = Console.ReadLine();

try

{

result = double.Parse(userInput);

}

catch

{

result = null;

}

return result;

}

}

(5) 執行應用程序,給兩個矢量(vector)輸入值,結果如圖12-1所示。

圖 12-1

(6) 再次執行應用程序,這次跳過四個值中的至少一個,結果如圖12-2所示。

圖 12-2

示例的說明

在這個示例中,創建了一個類Vector,它表示帶極坐標(有一個幅值和一個角度)的矢量,如圖12-3所示。

圖 12-3

坐標r和_在代碼中用公共字段R和Theta表示,其中Theta的單位是度(°)。ThetaRad用於獲取Theta的弧度值,這是必須的,因為Math類在其靜態方法中使用弧度。R和Theta的類型都是double?,所以它們可以為空。

public class Vector

{

public double? R = null;

public double? Theta = null;

 

public double? ThetaRadians

{

get

{

// Convert degrees to radians.

return (Theta * Math.PI / 180.0);

}

}

Vector的構造函數標准化R和Theta的初始值,然後賦予公共字段。

public Vector(double? r, double? theta)

{

// Normalize.

if (r < 0)

{

r = -r;

theta += 180;

}

theta = theta % 360;

 

// Assign fIElds.

R = r;

Theta = theta;

}

Vector類的主要功能是使用運算符重載對矢量進行相加和相減,這需要一些非常基本的三角函數知識,這裡不解釋它們。在代碼中,重要的是,如果在獲取R或ThetaRad的Value屬性時拋出了異常,即其中一個是null,就返回“空”矢量。

public static Vector Operator +(Vector op1, Vector op2)

{

try

{

// Get (x, y) coordinates for new vector.

...

}

catch

{

// Return "null" vector.

return new Vector(null, null);

}

}

如果組成矢量的坐標是null,該矢量就是無效的,這裡用R和Theta都可為null的Vector類來表示。

Vector類的其他代碼重寫了其他運算符,把相加的功能擴展到相減上,再重寫ToString(),獲取Vector對象的字符串表示。

Program.cs中的代碼測試Vector類,讓用戶初始化兩個矢量,再對它們進行相加和相減。如果用戶省略了一個值,該值就解釋為null,應用前面提及的規則。 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved