今天就跟大家聊聊有關(guān)在NullObject中C#6.0有哪些改進(jìn),可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
網(wǎng)站設(shè)計(jì)制作、網(wǎng)站制作,成都做網(wǎng)站公司-創(chuàng)新互聯(lián)建站已向1000多家企業(yè)提供了,網(wǎng)站設(shè)計(jì),網(wǎng)站制作,網(wǎng)絡(luò)營(yíng)銷等服務(wù)!設(shè)計(jì)與技術(shù)結(jié)合,多年網(wǎng)站推廣經(jīng)驗(yàn),合理的價(jià)格為您打造企業(yè)品質(zhì)網(wǎng)站。
什么是空引用異常
作為一個(gè)敲過(guò)代碼的碼農(nóng)來(lái)說(shuō),似乎沒(méi)有誰(shuí)沒(méi)有遇到過(guò)NullReferenceException這 個(gè)問(wèn)題,有些時(shí)候當(dāng)方法內(nèi)部調(diào)用一個(gè)屬性、方法(委托)時(shí),我們控制這些屬性在“外部”的表現(xiàn)(當(dāng)然某些情況下使用ref關(guān)鍵字除外),所以我們要在方法 的內(nèi)部去判斷屬性、委托方法是否為Null來(lái)避免可能的、錯(cuò)誤使用上帶來(lái)的空引用異常,這樣當(dāng)我們知道如果對(duì)象為Null的話,我們會(huì)實(shí)現(xiàn)符合我們“預(yù) 期”的行為。
解決空引用異常---Check Any Where
這很簡(jiǎn)單,我只要在需要用的地方檢查一下是否為Null就可以了。是的,這非常簡(jiǎn)單,語(yǔ)義也很清晰,但是當(dāng)你要重復(fù)檢查一個(gè)對(duì)象實(shí)體10000萬(wàn)次時(shí),你的代碼中將存在10000個(gè)如下代碼段:
public void Check() { if (Person.AlivePerson() != null) { Person.AlivePerson().KeepAlive = true; } }
你能容忍這樣的行為嗎?
If(OK)
Continue;
Else
Close;
應(yīng)用NullObject設(shè)計(jì)模式
NullObjectPattern出自forth by Gamma(設(shè)計(jì)模式4人組),核心內(nèi)容是:提供一個(gè)給定對(duì)象的空值代理,空值代理中提供不做任何事情的方法實(shí)現(xiàn)。
接下來(lái)讓我們看看維基百科上的C#實(shí)現(xiàn):
// compile as Console Application, requires C# 3.0 or higher using System; using System.Linq; namespace MyExtensionWithExample { public static class StringExtensions { public static int SafeGetLength(this string valueOrNull) { return (valueOrNull ?? string.Empty).Length; } } public static class Program { // define some strings static readonly string[] strings = new [] { "Mr X.", "Katrien Duck", null, "Q" }; // write the total length of all the strings in the array public static void Main(string[] args) { var query = from text in strings select text.SafeGetLength(); // no need to do any checks here Console.WriteLine(query.Sum()); // The output will be: 18 } } }
在C#語(yǔ)言中,我們通過(guò)靜態(tài)的擴(kuò)展方法來(lái)實(shí)現(xiàn)將檢查方式統(tǒng)一在方法內(nèi)部,而不是寫的到處都是,上面的例子中是在String類上實(shí)現(xiàn)了一個(gè)SafeGetLength擴(kuò)展方法,將為所有String類型提供了一個(gè)方法,這樣我們?cè)凇按a整潔”上又進(jìn)了一步。
下面我們?cè)賮?lái)看一個(gè)更常用的例子---來(lái)自于StackOverFlow
public static class EventExtensions { public static void Raise(this EventHandler evnt, object sender, T args) where T : EventArgs { if (evnt != null) { evnt(sender, args); } } }
***,說(shuō)一個(gè)細(xì)節(jié)問(wèn)題,以上代碼均沒(méi)有實(shí)現(xiàn)“線程安全”,在大牛Eric Lippert的文章中針對(duì)線程安全有過(guò)一個(gè)更精彩的討論,請(qǐng)戳這里。
改進(jìn)后的代碼時(shí)在方法內(nèi)部增加了一個(gè)臨時(shí)變量,作為方法內(nèi)部的拷貝,實(shí)現(xiàn)線程安全,如果有疑問(wèn)請(qǐng)參考我的《C#堆vs?!分袑?duì)方法內(nèi)部變量在堆棧上的表現(xiàn)一章。
public class SomeClass { public event EventHandlerMyEvent; private void DoSomething() { var tmp = MyEvent; tmp.Raise(this, EventArgs.Empty); } }
更“潮”的方式-C#6.0語(yǔ)法
來(lái)自MSDN Magazine的Mark Michaelis(《C#本質(zhì)論》作者)給我們介紹了C#6.0在語(yǔ)言可能帶來(lái)的新改進(jìn),其中就有針對(duì)“Null條件運(yùn)算符”的改進(jìn)。
C#6.0更多參考:
Part One: https://msdn.microsoft.com/zh-cn/magazine/dn683793.aspx
Part Two: https://msdn.microsoft.com/zh-cn/magazine/dn802602.aspx
即使是 .NET 開(kāi)發(fā)新手,也可能非常熟悉 NullReferenceException。有一個(gè)例外是幾乎總是會(huì)指出一個(gè) Bug,因?yàn)殚_(kāi)發(fā)人員在調(diào)用 (null) 對(duì)象的成員之前未進(jìn)行充分的 null 檢查。請(qǐng)看看以下示例:
public static string Truncate(string value, int length) { string result = value; if (value != null) // Skip empty string check for elucidation { result = value.Substring(0, Math.Min(value.Length, length)); } return result; }
如果不進(jìn)行 null 檢查,此方法會(huì)引發(fā) NullReferenceException。盡管這很簡(jiǎn)單,但檢查字符串參數(shù)是否為 null 的過(guò)程卻稍微有些繁瑣。通常,考慮到比較的頻率,該繁瑣的方法可能沒(méi)有必要。C# 6.0 包括一個(gè)新的 null 條件運(yùn)算符,可幫助您更加簡(jiǎn)便地編寫這些檢查:
public static string Truncate(string value, int length) { return value?.Substring(0, Math.Min(value.Length, length)); } [TestMethod] public void Truncate_WithNull_ReturnsNull() { Assert.AreEqual(null, Truncate(null, 42)); }
根據(jù) Truncate_WithNull_ReturnsNull 方法所演示的內(nèi)容,如果對(duì)象的值實(shí)際上為 null,則 null 條件運(yùn)算符將返回 null。這帶來(lái)了一個(gè)問(wèn)題,即 null 條件運(yùn)算符在調(diào)用鏈中出現(xiàn)時(shí)會(huì)是什么情況?如以下示例中所示:
public static string AdjustWidth(string value, int length) { return value?.Substring(0, Math.Min(value.Length, length)).PadRight(length); } [TestMethod] public void AdjustWidth_GivenInigoMontoya42_ReturnsInigoMontoyaExtended() { Assert.AreEqual(42, AdjustWidth("Inigo Montoya", 42).Length); }
盡管 Substring 是通過(guò) null 條件運(yùn)算符進(jìn)行調(diào)用的,并且 null value?.Substring 似乎返回了 null,但語(yǔ)言行為按您的想法進(jìn)行。這簡(jiǎn)化了對(duì) PadRight 的調(diào)用過(guò)程,并立即返回 null,從而避免會(huì)導(dǎo)致出現(xiàn) NullReferenceException 的編程錯(cuò)誤。這個(gè)概念稱為“null 傳播”。
Null 條件運(yùn)算符會(huì)根據(jù)具體條件進(jìn)行 null 檢查,然后再調(diào)用目標(biāo)方法以及調(diào)用鏈中的所有其他方法。這將可能產(chǎn)生一個(gè)令人驚訝的結(jié)果,例如,text?.Length.GetType 語(yǔ)句中的結(jié)果。
如果 null 條件運(yùn)算符在調(diào)用目標(biāo)為 null 時(shí)返回 null,那么調(diào)用會(huì)返回值類型的成員時(shí)最終會(huì)是什么數(shù)據(jù)類型(假定值類型不能為 null)?例如,從 value?.Length 返回的數(shù)據(jù)類型不能只是 int。答案當(dāng)然是:可以為 null 的類型(int?)。實(shí)際上,嘗試僅將結(jié)果分配給 int 將會(huì)出現(xiàn)編譯錯(cuò)誤:
int length = text?.Length; // Compile Error: Cannot implicitly convert type 'int?' to 'int'
Null 條件具有兩種語(yǔ)法形式。首先,問(wèn)號(hào)在點(diǎn)運(yùn)算符前面 (?.)。其次,將問(wèn)號(hào)和索引運(yùn)算符結(jié)合使用。例如,給定一個(gè)集合(而非在索引到集合之前顯式進(jìn)行 null 檢查),您就可以使用 null 條件運(yùn)算符執(zhí)行此操作:
public static IEnumerableGetValueTypeItems ( IList collection, params int[] indexes) where T : struct { foreach (int index in indexes) { T? item = collection?[index]; if (item != null) yield return (T)item; } }
此示例使用了運(yùn)算符 ?[…] 的 null 條件索引形式,導(dǎo)致僅在集合不為 null 時(shí)才索引到集合。通過(guò) null 條件運(yùn)算符的此形式,T? item = collection?[index] 語(yǔ)句在行為上相當(dāng)于:
T? item = (collection != null) ? collection[index] : null.
請(qǐng)注意,null 條件運(yùn)算符僅可檢索項(xiàng)目,不會(huì)分配項(xiàng)目。如果給定 null 集合,那么這意味著什么?
請(qǐng)注意針對(duì)引用類型使用 ?[…] 時(shí)的隱式歧義。由于引用類型可以為 null,因此對(duì)于集合是否為 null,或者是否元素本身實(shí)際上就是 null 而言,來(lái)自 ?[…] 運(yùn)算符的 null 結(jié)果不明確。
Null 條件運(yùn)算符的一個(gè)非常有用的應(yīng)用程序解決了 C# 自 C# 1.0 以來(lái)一直存在的的一個(gè)特性,即在調(diào)用委托之前檢查是否為 null。我們來(lái)看一下圖中顯示的 C# 2.0 代碼。
圖 1 在調(diào)用委托之前檢查是否為 Null
class Theremostat { event EventHandlerOnTemperatureChanged; private int _Temperature; public int Temperature { get { return _Temperature; } set { // If there are any subscribers, then // notify them of changes in temperature EventHandler localOnChanged = OnTemperatureChanged; if (localOnChanged != null) { _Temperature = value; // Call subscribers localOnChanged(this, value); } } } }
通過(guò)使用 null 條件運(yùn)算符,整個(gè) set 實(shí)現(xiàn)過(guò)程就可簡(jiǎn)化為:
OnTemperatureChanged?.Invoke(this, value)
現(xiàn)在,您只需對(duì)將 null 條件運(yùn)算符作為前綴的 Invoke 進(jìn)行調(diào)用,不再需要將委托實(shí)例分配給本地變量,從而實(shí)現(xiàn)線程安全,甚至是在調(diào)用委托之前顯式檢查值是否為 null。
C# 開(kāi)發(fā)人員都很想知道在***的四個(gè)版本中是否對(duì)此內(nèi)容有所改進(jìn)。答案是最終進(jìn)行了改進(jìn)。僅此一項(xiàng)功能就可以改變調(diào)用委托的方式。
另一個(gè) null 條件運(yùn)算符普及的常見(jiàn)模式是與 coalesce 運(yùn)算符結(jié)合使用。您無(wú)需在調(diào)用 Length 之前對(duì) linesOfCode 進(jìn)行 null 檢查,而是可以編寫項(xiàng)目計(jì)數(shù)算法,如下所示:
List
在這種情況下,任何空集合(無(wú)項(xiàng)目)和 null 集合均標(biāo)準(zhǔn)化為返回相同數(shù)量??傊琻ull 條件運(yùn)算符將實(shí)現(xiàn)以下功能:
1. 如果操作數(shù)為 null,則返回 null
2. 如果操作數(shù)為 null,則簡(jiǎn)化調(diào)用鏈中的其他調(diào)用
3. 如果目標(biāo)成員返回一個(gè)值類型,則返回可以為 null 的類型 (System.Nullable
4. 以線程安全的方式支持委托調(diào)用
5. 可用作成員運(yùn)算符 (?.) 和索引運(yùn)算符 (?[…])
看完上述內(nèi)容,你們對(duì)在NullObject中C#6.0有哪些改進(jìn)有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。