最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

從零開始獨(dú)立游戲開發(fā)學(xué)習(xí)筆記(十七)--Unity學(xué)習(xí)筆記(六)--微軟C#指南(三)

2021-11-28 16:26 作者:oyishyi  | 我要投稿

好了,忙的時(shí)候結(jié)束了。

繼續(xù)講述和對象相關(guān)的知識。這一章講使用模式匹配進(jìn)行類型轉(zhuǎn)換。

1. 如何安全地格式轉(zhuǎn)換(模式匹配)

由于對象具有多態(tài)性。一個(gè)具有基類類型的變量是可以存放 derived 類型的變量的值的,但這有可能產(chǎn)生 InvallidCastException。C# 提供了使用模式匹配(pattern match)的格式轉(zhuǎn)換(cast),僅當(dāng)成功的時(shí)候會轉(zhuǎn)換。C# 也提供了?is?和?as?關(guān)鍵字來判斷一個(gè)值是否為某個(gè)類型。

1.1?is?運(yùn)算符

比如說以下代碼:

static void FeedMammal(Animal a) { ? ?if (a is Mammmal m) ? ?{ ? ? ? ?m.Eat(); ? ?} ? ?else ? ?{ ? ? ? ?Console.WriteLine($"{a.GetType().Name} is not a Mammal"); ? ?} }

重點(diǎn)在于:

  1. is?后面并不只是一個(gè)類型,而是聲明了一個(gè) Mammal 類型的變量。并不是說只能這么寫。單單寫?a is Mammal?也行,只是這種語法把類型判斷和初始化寫在一起,也是可行的一種語法。當(dāng)判斷成功的時(shí)候,a 的值會被賦予給了 m。

  2. m 的作用域僅僅在于 if 里,甚至連 else 里都無法訪問。

1.2?as?運(yùn)算符

請看以下代碼:

static void TestForMammals(Object o) { ? ?var m = o as Mammal; ? ?if (m != null) ? ?{ ? ? ? ?Console.WriteLine(m.ToString()); ? ?} ? ?else ? ?{ ? ? ? ?Console.WriteLine($"{o.GetType().Name} is not a Mammal"); ? ?} }

  1. as?運(yùn)算符執(zhí)行一次轉(zhuǎn)換。如果成功則轉(zhuǎn)換成對應(yīng)類型,不成功則返回 null。

  2. 順便一提,上面的?m != null?也可以換成?m is not null

1.3 switch 做類型匹配

如下所示的語法也是可以的:

static void PatternMatchingSwitch(System.ValueType val){ ? ?switch (val) ? ?{ ? ? ? ?case int number: ? ? ? ? ? ?Console.WriteLine(number); ? ? ? ? ? ?break; ? ? ? ?case long number: ? ? ? ? ? ?Console.WriteLine(number); ? ? ? ? ? ?break; ? ? ? ?case decimal number: ? ? ? ? ? ?Console.WriteLine(number); ? ? ? ? ? ?break; ? ? ? ?case float number: ? ? ? ? ? ?Console.WriteLine(number); ? ? ? ? ? ?break; ? ? ? ?case double number: ? ? ? ? ? ?Console.WriteLine(number); ? ? ? ? ? ?break; ? ? ? ?case null: ? ? ? ? ? ?Console.WriteLine("val is a nullable type with the null value"); ? ? ? ? ? ?break; ? ? ? ?default: ? ? ? ? ? ?Console.WriteLine("Could not convert " + val.ToString()); ? ? ? ? ? ?break; ? ?} }

2. 模式匹配的場景

現(xiàn)代開發(fā)經(jīng)常要用到來自各種不同地方的數(shù)據(jù)源,因此數(shù)據(jù)類型也都不一致。
于是文章采用了這么一個(gè)場景--在一個(gè)收費(fèi)站收費(fèi)。根據(jù)高峰期和車型收費(fèi)。 難點(diǎn)在于,數(shù)據(jù)來源可能是多個(gè)不同的外部系統(tǒng)。那么首先假設(shè)有這么三個(gè)系統(tǒng)(3 個(gè) namespace):

namespace ConsumerVehicleRegistration{ ? ?public class Car ? ?{ ? ? ? ?public int Passengers { get; set; } ? ?} }namespace CommercialRegistration{ ? ?public class DeliveryTruck ? ?{ ? ? ? ?public int GrossWeightClass { get; set; } ? ?} }namespace LiveryRegistration{ ? ?public class Taxi ? ?{ ? ? ? ?public int Fares { get; set; } ? ?} ? ?public class Bus ? ?{ ? ? ? ?public int Capacity { get; set; } ? ? ? ?public int Riders { get; set; } ? ?} }

即,數(shù)據(jù)可能以不同的 class 形式存在。

2.1 最基礎(chǔ)的收費(fèi)

寫一個(gè)最基礎(chǔ)的收費(fèi)類:

using System;using CommercialRegistration;using ConsumerVehicleRegistration;using LiveryRegistration;namespace toll_calculator{ ? ?public class TollCalculator ? ?{ ? ? ? ?public decimal CalculateToll(object vehicle) => ? ? ? ? ? ?vehicle switch ? ? ? ?{ ? ? ? ? ? ?Car c ? ? ? ? ? => 2.00m, ? ? ? ? ? ?Taxi t ? ? ? ? ?=> 3.50m, ? ? ? ? ? ?Bus b ? ? ? ? ? => 5.00m, ? ? ? ? ? ?DeliveryTruck t => 10.00m, ? ? ? ? ? ?{ } ? ? ? ? ? ? => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)), ? ? ? ? ? ?null ? ? ? ? ? ?=> throw new ArgumentNullException(nameof(vehicle)) ? ? ? ?}; ? ?} }

這里使用了一個(gè) switch expression 的語法(非 switch statement)。語法一看大概也知道是怎么回事。因?yàn)檎麄€(gè)是一個(gè) switch,因此 => 跟的就是 return 的值。

  1. { } 則是匹配所有的 非 null 的 object。必須寫在后面,否則就被第一個(gè)返回了。

  2. null 則是匹配 null。

2.2 根據(jù)乘客收費(fèi)

為了減少流量,讓車輛載客數(shù)更高,因此希望乘客越少收費(fèi)越高。

我們可以改寫上面的代碼:

public class TollCalculator ? ?{ ? ? ? ?public decimal CalculateToll(object vehicle) => ? ? ? ? ? ?vehicle switch ? ? ? ?{ ? ? ? ? ? ?Car {Passengers: 0} => 2.00m + 0.50m, ? ? ? ? ? ?Car {Passengers: 1} => 2.0m, ? ? ? ? ? ?Car {Passengers: 2} => 2.0m - 0.50m, ? ? ? ? ? ?Car => 2.00m - 1.0m, ? ? ? ? ? ? ? ? ? ? ? ?Taxi {Fares: 0} => 3.50m + 1.00m, ? ? ? ? ? ?Taxi {Fares: 1} => 3.50m, ? ? ? ? ? ?Taxi {Fares: 2} => 3.50m - 0.50m, ? ? ? ? ? ?Taxi => 3.50m - 1.00m, ? ? ? ? ? ? ? ? ? ? ? ?Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m, ? ? ? ? ? ?Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m, ? ? ? ? ? ?Bus => 5.00m, ? ? ? ? ? ? ? ? ? ? ? ?DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m, ? ? ? ? ? ?DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m, ? ? ? ? ? ?DeliveryTruck => 10.00m, ? ? ? ? ? ? ? ? ? ? ? ?{ } ? ? ? ? ? ? => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)), ? ? ? ? ? ?null ? ? ? ? ? ?=> throw new ArgumentNullException(nameof(vehicle)) ? ? ? ?}; ? ?}

  1. when?的用法也是簡潔明了。當(dāng)并等于某一個(gè)值,而是一個(gè)判斷語句的時(shí)候用 when。

  2. 以上的代碼有部分比較重復(fù)。比如對于 car 和 taxi,每個(gè)乘客數(shù)量都要寫一整行代碼??梢员缓喕癁橐韵麓a:

public decimal CalculateToll(object vehicle) => ? ?vehicle switch ? ?{ ? ? ? ?Car c => c.Passengers switch ? ? ? ?{ ? ? ? ? ? ?0 => 2.00m + 0.5m, ? ? ? ? ? ?1 => 2.0m, ? ? ? ? ? ?2 => 2.0m - 0.5m, ? ? ? ? ? ?_ => 2.00m - 1.0m ? ? ? ?}, ? ? ? ?Taxi t => t.Fares switch ? ? ? ?{ ? ? ? ? ? ?0 => 3.50m + 1.00m, ? ? ? ? ? ?1 => 3.50m, ? ? ? ? ? ?2 => 3.50m - 0.50m, ? ? ? ? ? ?_ => 3.50m - 1.00m ? ? ? ?}, ? ? ? ?Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m, ? ? ? ?Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m, ? ? ? ?Bus b => 5.00m, ? ? ? ?DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m, ? ? ? ?DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m, ? ? ? ?DeliveryTruck t => 10.00m, ? ? ? ?{ } ?=> throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)), ? ? ? ?null => throw new ArgumentNullException(nameof(vehicle)) ? ?};

  1. 可以看到根本沒有新的語法。而是再寫一個(gè) switch expression。

  2. _ 表示匹配其他所有情況。同理也不能寫在前面,因?yàn)橐欢〞黄ヅ渖稀?/p>

2.3 根據(jù)高峰時(shí)間收費(fèi)

假設(shè)有這么一個(gè)需求。周末正常收費(fèi)。工作日的話,早上的入流量和晚上的出流量雙倍收費(fèi)。其他時(shí)間 1.5 倍收費(fèi)。凌晨則減少為 0.75。

如果寫成 if 語句,寫倒是可以寫,但是效果如下:

public decimal PeakTimePremiumIfElse(DateTime timeOfToll, bool inbound){ ? ?if ((timeOfToll.DayOfWeek == DayOfWeek.Saturday) || ? ? ? ?(timeOfToll.DayOfWeek == DayOfWeek.Sunday)) ? ?{ ? ? ? ?return 1.0m; ? ?} ? ?else ? ?{ ? ? ? ?int hour = timeOfToll.Hour; ? ? ? ?if (hour < 6) ? ? ? ?{ ? ? ? ? ? ?return 0.75m; ? ? ? ?} ? ? ? ?else if (hour < 10) ? ? ? ?{ ? ? ? ? ? ?if (inbound) ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?return 2.0m; ? ? ? ? ? ?} ? ? ? ? ? ?else ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?return 1.0m; ? ? ? ? ? ?} ? ? ? ?} ? ? ? ?else if (hour < 16) ? ? ? ?{ ? ? ? ? ? ?return 1.5m; ? ? ? ?} ? ? ? ?else if (hour < 20) ? ? ? ?{ ? ? ? ? ? ?if (inbound) ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?return 1.0m; ? ? ? ? ? ?} ? ? ? ? ? ?else ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?return 2.0m; ? ? ? ? ? ?} ? ? ? ?} ? ? ? ?else // Overnight ? ? ? ?{ ? ? ? ? ? ?return 0.75m; ? ? ? ?} ? ?} }

可以用,但非常難讀,也不好改。

2.3.1 使用模式匹配以及其他技巧來簡化代碼

僅僅使用模式匹配來匹配所有可能性也不好,依然復(fù)雜,因?yàn)槲覀冇泻芏喾N組合情況。

2.3.1.1 周末還是工作日

第一個(gè)條件是是否為周末。那么專門為此寫一個(gè)函數(shù):

// 注意 timeOfToll.DayOfWeek 和 DayOfWeek.Monday 中的 DayOfWeek 不是一個(gè)東西。// 前者是 DateTime 類型的一個(gè)屬性,后者是一個(gè) enum 類型。// 前者的值也為 DayOfWeek 類型public static bool IsWeekday(DateTime timeOfToll) => ? ?timeOfToll.DayOfWeek switch { ? ? ? ?DayOfWeek.Monday => true, ? ? ? ?DayOfWeek.Tuesday => true, ? ? ? ?DayOfWeek.Wednesday => true, ? ? ? ?DayOfWeek.Thursday => true, ? ? ? ?DayOfWeek.Friday => true, ? ? ? ?DayOfWeek.Saturday => false, ? ? ? ?DayOfWeek.Sunday => false ? ?}

還可以再簡化:

public static bool IsWeekday(DateTime timeOfToll) => ? ?timeOfToll.DayOfWeek switch { ? ? ? ?DayOfWeek.Saturday => false, ? ? ? ?DayOfWeek.Sunday => false, ? ? ? ?_ => true ? ?}

2.3.1.2 一天的時(shí)間段

先看代碼:

public enum TimeBand { ? ?MorningRush, ? ?Daytime, ? ?EvenignRush, ? ?Overnight }public static TimeBand GetTimeBand(DateTime timeOfToll) => ? ?timeOfToll.Hour switch ? ?{ ? ? ? ?> 19 or < 6 => TimeBand.Overnight, ? ? ? ?< 10 => TimeBand.MorningRush, ? ? ? ?> 16 => TimeBand.EvenignRush, ? ? ? ?_ => TimeBand.Daytime ? ?};

  1. 使用了 enum 來將一天的多個(gè)時(shí)間段分配值。

  2. 使用了?> 19 or < 6?這種語法,>?和?<?以及?or?都是在 C# 9.0 后引入的。當(dāng)然還有?>=,<=and,not?這些語法。(什么你問為什么沒有?=?的語法,因?yàn)椴恍枰?,直接?6 就是 =6 了)

2.3.1.3 最終代碼

有了以上兩個(gè)函數(shù)后,代碼就可以簡化為這種 tuple pattern 形式:

public static decimal CalculateToll(DateTime timeOfToll, bool isInbound) => ? ?(IsWeekday(timeOfToll), GetTimeBand(timeOfToll), isInbound) switch ? ?{ ? ? ? ?(true, TimeBand.MorningRush, true) => 2.00m, ? ? ? ?(true, TimeBand.MorningRush, false) => 1.00m, ? ? ? ?(true, TimeBand.Daytime, true) => 1.50m, ? ? ? ?(true, TimeBand.Daytime, false) => 1.50m, ? ? ? ?(true, TimeBand.EveningRush, true) => 1.00m, ? ? ? ?(true, TimeBand.EveningRush, false) => 2.00m, ? ? ? ?(true, TimeBand.Overnight, true) => 0.75m, ? ? ? ?(true, TimeBand.Overnight, false) => 0.75m, ? ? ? ?(false, TimeBand.MorningRush, true) => 1.00m, ? ? ? ?(false, TimeBand.MorningRush, false) => 1.00m, ? ? ? ?(false, TimeBand.Daytime, true) => 1.00m, ? ? ? ?(false, TimeBand.Daytime, false) => 1.00m, ? ? ? ?(false, TimeBand.EveningRush, true) => 1.00m, ? ? ? ?(false, TimeBand.EveningRush, false) => 1.00m, ? ? ? ?(false, TimeBand.Overnight, true) => 1.00m, ? ? ? ?(false, TimeBand.Overnight, false) => 1.00m, ? ?};

當(dāng)然,很多條件可以簡化:

public static decimal CalculateToll(DateTime timeOfToll, bool isInbound) => ? ?(IsWeekday(timeOfToll), GetTimeBand(timeOfToll), isInbound) switch ? ?{ ? ? ? ?(true, TimeBand.MorningRush, true) => 2.00m, ? ? ? ?(true, TimeBand.MorningRush, false) => 1.00m, ? ? ? ?(true, TimeBand.Daytime, _) => 1.50m, ? ? ? ?(true, TimeBand.EveningRush, true) => 1.00m, ? ? ? ?(true, TimeBand.EveningRush, false) => 2.00m, ? ? ? ?(true, TimeBand.Overnight, _) => 0.75m, ? ? ? ?(false, _, _) => 1.00m, ? ?};

然后可以把 3 個(gè)返回 1.00m 的用 _ 代替:

public static decimal CalculateToll(DateTime timeOfToll, bool isInbound) => ? ?(IsWeekday(timeOfToll), GetTimeBand(timeOfToll), isInbound) switch ? ?{ ? ? ? ?(true, TimeBand.MorningRush, true) => 2.00m, ? ? ? ?(true, TimeBand.Daytime, _) => 1.50m, ? ? ? ?(true, TimeBand.EveningRush, false) => 2.00m, ? ? ? ?(true, TimeBand.Overnight, _) => 0.75m, ? ? ? ?_ => 1.00m, ? ?};


從零開始獨(dú)立游戲開發(fā)學(xué)習(xí)筆記(十七)--Unity學(xué)習(xí)筆記(六)--微軟C#指南(三)的評論 (共 條)

分享到微博請遵守國家法律
温宿县| 武功县| 施秉县| 玛多县| 葫芦岛市| 甘德县| 综艺| 青州市| 新巴尔虎右旗| 德庆县| 东丰县| 武安市| 饶阳县| 哈尔滨市| 阳东县| 淮南市| 中江县| 宜兰县| 沙洋县| 沙湾县| 德庆县| 凤城市| 灯塔市| 汾阳市| 固阳县| 吉林市| 绿春县| 丹棱县| 嵩明县| 迭部县| 大同县| 同德县| 宜兴市| 永福县| 九江市| 美姑县| 元朗区| 郴州市| 北票市| 万载县| 琼海市|