類型轉換的法則(NET1.1+)

来源:http://www.cnblogs.com/shouce/archive/2016/03/15/5277943.html
-Advertisement-
Play Games

【問】 在C#和Visual Basic的轉換中,以下一些轉換的用法和區別是什麼呢? [C#] [VB.NET] 【錯誤回答】 沒有區別,因為運行了之後都可以正常轉化。 【正解】 光從運行結果來看當然是毫無區別,因為題目所給出的僅僅是一部分的例子,不是全部。許多初學者容易產生“以偏概全”的錯誤認識。


【問】

在C#和Visual Basic的轉換中,以下一些轉換的用法和區別是什麼呢?

[C#]

long num = 12;
int n = (int)num;
int m = Convert.ToInt32(num);

[VB.NET]

Dim num As Long = 12
Dim n As Integer = num
n = CInt(num)
n= CType(num,Integer)
n = Convert.ToInt32(num)

【錯誤回答】

沒有區別,因為運行了之後都可以正常轉化。

【正解】

光從運行結果來看當然是毫無區別,因為題目所給出的僅僅是一部分的例子,不是全部。許多初學者容易產生“以偏概全”的錯誤認識。讓我們先以C#作為例子進行比較:

[C#]

long num = long.MaxValue;
int n = (int)num; …………………………①
int m = Convert.ToInt32(num); ………②

註意①處的例子,因為我要把一個long類型賦值給int類型。因此語法上VS認為不能直接轉換,除非你使用強制類型轉換聲明。同時又因為long.MaxValue已經遠遠大於int所可以容納的範圍,因此實際存入n中的數值是溢出的(即不是原來的值)。

如果註釋掉①句,單獨運行②的話你會發現有一個錯誤——提示你說num大於int的上限或者下限了,而單獨運行①只是因為字元串被截斷,沒有錯誤提示。顯然地,Convert的轉換原則是直接檢查被轉換的這個變數(本題為num)實際存放的數值是否可以被另一個接收方(本題為m)接受——如果超出了m類型所容納的範圍(即int的MaxValue和MinValue),即被視為錯誤。

在VB.NET中同樣地我們來做一個實驗:

Dim num As Long = Long.MaxValue
Dim n As Integer = num…………………①
n = CInt(num) …………………………②
n= CType(num,Integer) ………………③
n = Convert.ToInt32(num)

結果你發現無論註釋掉其它表達式,單獨運行①、②或是③的任何一句,都會拋出“算術運算導致溢出”的錯誤。可見在VB.NET中,在語法層面檢測可以進行轉換之後,直接檢測被轉換的那個數值是否可以被接收方容納。如果不可容納,則拋出異常,視為錯誤。

附帶說一句——實際上,C#在強制轉換中也可以進行類似VB.NET一樣的溢出檢測,只不過預設C#是啟用unchecked機制你完全可以使用checked機制來達到類似Convert轉換一樣的效果。比如:

[C#]

checked
{
long num = long.MaxValue;
int n = (int)num; //運行到此立即引發一個錯誤,表示結果溢出
}

【總結】

1 C#中普通強制類型轉換(類似C#例子中第一句),預設只進行語法檢測,對實際轉換的值是否可以被接受不做檢測,除非顯式地放入“checked”塊中。

2 C#VB.NETConvert語句,不僅對轉換的類型進行檢測(因為Convert.ToXXX“XXX”就是需要轉換後的類型),而且還檢測了被轉換變數中那個數值是否可以完全被轉換後的變數容納。

3C#語法嚴謹,因此從大類型=>小類型轉換時候必須顯式聲明;相對而言,VB.NET並不需要如此嚴謹的語法。對於VB.NET而言,直接賦值“A=B”(相當於A=CType(B,A))就可以了。對於值類型而言,B必須認為定義隱式或者是顯式轉換函數(下麵有)。但CType對於“類”而言在語法上相當於A a =(A)B,是要嚴格檢查B是否可以轉換成A(兩種情況:要麼B是A的父類,要麼是自定義隱式/顯式從B=>A的轉換,否則會引發語法錯誤;另外對於第一種而言,如果要保證運行不出錯,B里包含的實體類至少是A或者其子類)。

4)另外VB.NET還有一個DirectCast轉化,它接受的兩個對象都必須是類,不能是結構(即便結構自定義隱式/顯式轉換也不行)。

【拓展】

一、自定義顯式轉換和隱式轉換:

我們目前知道一個事實——那就是.NET框架體系中(C#和VB.NET)而言,“類型轉換”中是按照“里氏原則”的——即子類向父類轉換;也就是說,毫無關係的類是無法進行強制轉換的。那麼現在的問題在於:int,double等都是結構(結構是不允許被繼承的),它們是怎樣實現互相轉換的?實際上,C#或者VB.NET允許為兩個毫不相干的類型(這裡是指代“無繼承關係”的)人為指定隱式轉換和顯式轉換。其語法分別為:

[C#]

public static implicit/exclipt operator 轉換後類型(轉換前類型 變數名)

[VB.NET]

Public Shared Widening/Narrowing Operator CType(轉換前類型 變數名) As轉換後類型

因為這個方法是自定義賦值符號(=),不屬於任何類的實例,比較特殊,有些近似於“運算符重載”,因此是靜態方法,一旦運行時立即載入並且自動被調用。“implicit”隱式轉換,“exclipit”顯式轉換(定義了exclipit方法的時候,調用必須寫成:xxx = (轉換後類型)變數名,就像long=>int必須寫成(int)long變數的形式)。

任意一個類都從其先天的父類“object”直接繼承了ToString()方法,但是預設是不重寫這個方法的。因此返回的也都是“命名空間.類名”。我們不希望這樣做,最好的結果是我們如果直接把這個類賦值給一個String,然後通過String就輸出其主要內容。下麵就通過這個簡單的“複數類”說明問題:

[C#]

複製代碼
public class Complex
{
public double Real { get; set; }
public double Comnum { get; set; }

public static implicit operator string(Complex c)
{
// 準備轉化後輸出的字元串變數
string result = string.Empty;

// 如果實數部分不等於0

if (c.Real != 0)
{
//拼接實數部分
result += c.Real.ToString();
}

//虛數部分,如果不等於0

if (c.Comnum != 0)
{
//如果虛數部分小於0,連同負號一起輸出
if (c.Comnum < 0)
{
result += c.Comnum+"i";
}
//如果虛數部分大於0
else if(c.Comnum>0)
{
//如果實數等於0的話,直接輸出虛數部分
if (c.Real == 0.0)
{
result = c.Comnum.ToString()+"i";
}
//否則前面輸出帶有加號的
else
{
result += "+" + c.Comnum+"i";

}
}
}
return result;
}
}
複製代碼

[VB.NET]

複製代碼
Public Class Complex
Public Property Real() As Double
Get
Return m_Real
End Get
Set
m_Real = Value
End Set
End Property
Private m_Real As Double
Public Property Comnum() As Double
Get
Return m_Comnum
End Get
Set
m_Comnum = Value
End Set
End Property
Private m_Comnum As Double

Public Shared Widening Operator CType(c As Complex) As String
' 準備轉化後輸出的字元串變數
Dim result As String = String.Empty
' 如果實數部分不等於0
If c.Real <> 0 Then
'拼接實數部分
result += c.Real.ToString()
End If

'虛數部分,如果不等於0
If c.Comnum <> 0 Then
'如果虛數部分小於0,連同負號一起輸出
If c.Comnum < 0 Then
result += c.Comnum + "i"
'如果虛數部分大於0
ElseIf c.Comnum > 0 Then
'如果實數等於0的話,直接輸出虛數部分
If c.Real = 0.0 Then
result = c.Comnum.ToString() & "i"
Else
'否則前面輸出帶有加號的
result += "+" + c.Comnum & "i"
End If
End If
End If
Return result
End Operator
End Class
複製代碼

調用的時候(寫法VS2008以上語法,你完全可以先創建實體,對實體屬性逐個賦值):

[C#]

複製代碼
string s = new Complex { Real = 1.2, Comnum = 2.3 };
Console.WriteLine(s);
s = new Complex { Real = 0, Comnum = -2.5 };
Console.WriteLine(s);
s = new Complex { Real = -2.5, Comnum = 0 };
Console.WriteLine(s);
s = new Complex { Real = -2.5, Comnum = -2.5 };
Console.WriteLine(s);
複製代碼

[VB.NET]

複製代碼
Dim s As String = New Complex() With { _
Key .Real = 1.2, _
Key .Comnum = 2.3 _
}
Console.WriteLine(s)
s = New Complex() With { _
Key .Real = 0, _
Key .Comnum = -2.5 _
}
Console.WriteLine(s)
s = New Complex() With { _
Key .Real = -2.5, _
Key .Comnum = 0 _
}
Console.WriteLine(s)
s = New Complex() With { _
Key .Real = -2.5, _
Key .Comnum = -2.5 _
}
Console.WriteLine(s)
複製代碼

看到吧——一個自定義的Complex和string竟然可以轉化(註意:String是密封類,不可能存在任何其它子類的)。所以我們說——在未實現其它任何隱式或者顯式轉化情況下,預設兩個類之間的轉化必須遵照里氏原則(子類=>父類)進行

如果把上面示例中的“implicit”轉化成“exclipit”,那麼調用的時候(僅舉一例)——

string s = (string)new Complex { Real = 1.2, Comnum = 2.3 };

到這裡,我們就可以回答“拓展”中的那個問題了:之所以“毫不相干,沒有繼承”關係的結構可以互相轉化,是因為微軟也應用了類似手法,為每一個結構(例如Int32等)定義了“隱式”和“顯式”轉換方法。使得這些結構之間可以互相進行“顯式”或者“隱式”轉換。比如微軟為long定義了從long到int的顯式轉換法則,又為long到double定義了隱式轉換法則(當然,你把long到int定義成隱式的,把long到double定義成顯式的也未嘗不可)。所以這裡引申出來的一個問題在於——到底何時定義implicit和exclipt?

一般地,主要是按照異常原則進行判斷(所謂異常原則,就是說某個類型轉換成某個類型的時候如果肯定不會出錯或者造成數據丟失等情況,應當建議使用implicit;否則的話建議使用explicit(像int=>long恆成立,因為int只占用4個位元組,long是8個位元組,恆成立;但是反過來自然有溢出的可能了,或者轉換不正常情況)。

二、C#中“異常原則”在“語法層面”上轉化的實際應用:

說到轉換,因為C#遠遠比VB.NET複雜(須經過“語法”、“實際數據”兩步)。我們來看一下C#在“語法層面”上的轉換規則——一般地,大家可以按照以下規則記憶(1、其中sybe,int,long是帶符號的類型,byte,uint/char,ulong 是無符號的類型。2、從左到右,數值可以表示的範圍越來越大。其中char所能夠表示的範圍等同於uint,只是輸出的是字元):

sbyte      byte     short      ushort      int      uint/char    long          ulong         float     decimal     double   

1)帶符號之間的轉換法則:大=>小一律隱式,小=>大一律顯式。

2)無符號之間轉換法則:大=>小一律隱式,小=>大一律顯式。

3float,doubledecimal之間一律使用顯式轉換。

4)一切帶符號轉化到無符號(包括轉化到char)一律使用顯式轉換。

其實這些基本原則的總的一個母體都是從“異常原則”推導而來(例如:無符號因為沒有負數,把帶符號賦值給無符號可能引發數值異常等,所以需要強制轉換)。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一直以來對SQL SERVER的游標都不怎麼感冒,也很少使用SQL Server裡面的游標,前幾天有一位網友問如何檢查資料庫裡面沒有釋放的游標,覺得有點意思,就測試驗證了一下,順便整理於此。 會話1:我們模擬一個應用程式或腳本,在打開游標後,忘記關閉、釋放游標。 DECLARE Cursor_Tes...
  • LVS+Keepalived 介紹 LVS LVS是Linux Virtual Server的簡寫,意即Linux虛擬伺服器,是一個虛擬的伺服器集群系統。本項目在1998年5月由章文嵩博士成立,是中國國內最早出現的自由軟體項目之一。目前有三種IP負載均衡技術(VS/NAT、VS/TUN和VS/DR)
  • 負責維護公司產品的web伺服器搭建與維護,最近遇到一下狀況,今天在這裡簡單總結一下,希望對於剛剛一些剛入行的小伙伴有所幫助,避免再走彎路。 第一點:Tomcat記憶體設置: 一、常見的Java記憶體溢出有以下三種: 1. java.lang.OutOfMemoryError: Java heap spa
  •   線纜作為連接器件,相當於不同系統之間溝通的“橋梁”,選擇線纜類型的好壞,也決定著傳輸信號的質量,影響著整個系統的穩定性。 (1)特性阻抗   先說一下關於線纜在傳輸過程中的特性阻抗問題。   特性阻抗是指電纜無限長時該電纜所具有的阻抗,阻抗是阻止交流電流通的一種電阻,(所以萬用表測不能直接測出一
  •  
  •  
  • 最後要用一方法判斷ip地址是否正確,直接用.Net現成的類,方法如下:
  • 某一時候,為文本框(TextBox)裝飾個水印。它有兩種狀態,一是blur和focus。因此,我們可以在Javascript寫兩個事件: 演示:    
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...