C#的IS與AS運算子之撰寫方法

C#的IS在C# 7.0之後被賦予運算式(Expression)的能力後,事情開始變得複雜,透過此篇文章將IS與AS運算子的寫法複習一下,給日後有需要的人參考。

基本上IS運算子在目前被賦予三種撰寫模式:

  1. 類型模式:Type pattern
  2. 常數模式:Constant pattern
  3. Var模式:Var pattern

類型模式(Type Pattern)

這個模式基本上就是把類別Type拿出來比對,這對於介面類別用很大的程式碼很有用途,在這裡面我有偷渡了一下as的用法,若以類型Type比對的面向來說,基本上as的用法相對直覺很多,大家可以自己研究一下程式碼。

public static string Decision(IAnimal oTemp)
{
  if (oTemp is Cat oObj)
  { return oObj.cAction; }

  //展現出命中類別並接續判斷的寫法
  if (oTemp is Dog oDog1 && oDog1.cSay != "Woo")
  { return $"Cute dog say {oDog1.cSay}"; }

  //故意展示不out出臨時類別的寫法(比較麻煩要多寫一次轉型)
  if (oTemp is Dog)
  { return ((Dog)oTemp).cSay; }

  //AS語法的使用
  var oBird = oTemp as Bird;
  if (oBird != null)
  { return $"Fly {oBird.bFly}"; }

  //故意找不到烏龜
  return "Can not defined!";
}

調用這個方法的程式碼如下:

IAnimal oTemp;

//類型模式
oTemp = new Dog() { cName = "巴哥", cSay = "Awo" };
Console.WriteLine($"0. {Decision(oTemp)}");

oTemp = new Dog() { cName = "柴犬" };
Console.WriteLine($"1. {Decision(oTemp)}");

oTemp = new Cat() { cName = "摺耳貓" };
Console.WriteLine($"2. {Decision(oTemp)}");

oTemp = new Bird() { cName = "麻雀" };
Console.WriteLine($"3. {Decision(oTemp)}");

oTemp = new Turtle() { cName = "忍者龜" };
Console.WriteLine($"4. {Decision(oTemp)}");

oTemp = new Dog() { cName = "巴哥", cSay = "Awo" };
Console.WriteLine($"5. {Decision2(oTemp)}");

oTemp = new Dog() { cName = "柴犬" };
Console.WriteLine($"6. {Decision2(oTemp)}");

oTemp = new Cat() { cName = "摺耳貓" };
Console.WriteLine($"7. {Decision2(oTemp)}");

oTemp = new Bird() { cName = "麻雀" };
Console.WriteLine($"8. {Decision2(oTemp)}");

oTemp = new Turtle() { cName = "忍者龜" };
Console.WriteLine($"9. {Decision2(oTemp)}");

輸出結果:

0. Cute dog say Awo
1. Woo
2. Grab
3. Fly True
4. Can not defined!
5. Cute dog say Awo
6. Woo
7. Grab
8. Fly True
9. Just Turtle

使用C# 9.0 SwitchExpression重新改寫Decision函式

都已經練習到這田地了,當然是要採用SwitchExpression寫法來順便練習一下,下列函式與Decision的輸出結果是等效的。

public static string Decision2(IAnimal oTemp) => oTemp switch
{
  Cat x => x.cAction,
  Dog x when (x.cSay != "Woo") => $"Cute dog say {x.cSay}",
  Dog x => x.cSay,
  Bird x => $"Fly {x.bFly}",
  Turtle => "Just Turtle",
  _ => "Can not defined!"
};

可以比對一下新寫法,優點是真的精簡很多,缺點嘛.....。

常數模式(Constant Pattern)

IS在常數模式下可以被用來判斷常數變數(Const)、常數運算式、列舉(Enum,)、null等。

//常數模式
Console.WriteLine($@"ConstantPattern 1.: g = {IsMyLetter('g')}");
Console.WriteLine($@"ConstantPattern 2.: # = {IsMyLetter('#')}");
Console.WriteLine($@"ConstantPattern 3.: (""AAA"", 10) = {IsMyTuple(("AAA", 10))}");
Console.WriteLine($@"ConstantPattern 3.: (""BBB"", 30) = {IsMyTuple(("BBB", 30))}");

/* ***** 常數模式 ***** */
public static bool IsMyLetter(char x) => x 
  is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');

public static bool IsMyTuple((string, int) x) => x
  is ("AAA", 10) or ("BBB", 20);

輸出結果:

ConstantPattern 1.: g = True
ConstantPattern 2.: # = False
ConstantPattern 3.: ("AAA", 10) = True
ConstantPattern 3.: ("BBB", 30) = False

Var模式(Var Pattern)

var模式的優點是可以在Lambda運算式(Lambda Expression)中,將動態篩選過珍貴的運算結果進行暫時性的保存,並接續進行判斷運算,這樣的寫法在有需要的時刻,可以讓LINQ語法變得更精簡與易讀。

//var模式
Console.WriteLine($"VarPattern: {string.Join("、", IsVarPattern().ToArray())}");

/* ***** var模式 ***** */
public static IEnumerable<string> IsVarPattern()
{
  var oList = new System.Collections.Generic.List<string>() { "Abc", "Def", "Ghi", "Jkl" };
  return oList.Where(x => RepeatProcess(x).ToList() is var oTemps && oTemps.Count() > 2 && (oTemps.Where(y => y.StartsWith("Abc") || y.StartsWith("Ghi")).Count() > 0));
}

private static IEnumerable<string> RepeatProcess(string cTemp)
{
  var oRnd = new System.Random();
  for (int i = 0; i < oRnd.Next(1, 10); i++)
  { yield return $"{cTemp}|{i}"; }
}

上面的程式碼精華大致上就是當我們在LINQ中得到一個動態運算過的新集合,我們將其暫存在一個oTemps,接著我們可以用oTemps去進行一些運算判斷,讓我們可以更精確的過濾掉我們不想要的集合項目。(範例中的邏輯很沒有意義,請忽略!)

輸出結果:

VarPattern: Abc、Ghi(因為有亂數成分,故不一定是這個結果)

相關參考

C# 7.0 IsOperator AsOperator IsExpressionsTypePattern IsExpressionsConstantPattern IsExpressionsVarPattern