[英]C# replacing huge if-else statement
假設我有Item對象,它主要擁有這樣的枚舉屬性
public enum Shape
{
square = 1,
trangle = 2,
any = 3
}
public enum Color
{
blue = 1,
red = 2,
yellow = 3,
green = 4
}
public enum Material
{
glass = 1,
wood = 2,
metal = 3
}
public class Item
{
public Shape ItemShape { get; set; }
public Color ItemColor { get; set; }
public Material ItemMaterial { get; set; }
}
我想要做的是取決於整個三個屬性的組合,我需要稍后做一些動作;
我正在考慮使用if-else組合,如:
if(item.ItemShape == Shape.square && item.ItemColor == Color.blue && item.ItemMaterial == Material.glass)
{
//call Action1
}
else if(item.ItemShape == Shape.square && item.ItemColor == Color.blue && item.ItemMaterial == Material.wood)
{
// action2
}
......
問題是我有大約16個組合,所以它將是巨大的if-else方法來解決我應該稍后調用的方法。
也許是否有任何其他方法來替換if-else語句更可讀的代碼,設計模式或更高效的東西?
我想將整個可能的狀態組合為標記枚舉值,但不確定我是否可以稍后從對象屬性創建枚舉值。
聽起來你想要一些規則集來檢查項目。 我認為使這個更具可讀性的最簡單的形式是將項目,規則的屬性和操作傳遞給單獨的方法:
public bool RunActionIf(Item item, Shape shape, Color color, Material material, Action action)
{
if(item.ItemShape == shape && item.ItemColor == color && item.ItemMaterial == material)
{
action();
return true;
}
return false;
}
public void RunAction(Item item)
{
var result =
RunActionIf(item, Shape.square, Color.blue, Material.glass, Action1) ||
RunActionIf(item, Shape.square, Color.blue, Material.wood, Action2) ||
/* Implement your whole table like this */;
if(!result)
{
throw new ArgumentException("No matching rule found", nameof(item));
}
}
該方法的主要優點是它更短,並且聲明中的“開銷”更少。 你可以很容易地看到:形狀X +顏色Y +材料Z =那個動作。
另一個優點是實現某些異常更容易,例如允許其中一個規則參數為null
來指示任何顏色,或者使用Color.any來支持它,盡管我認為在枚舉中有any
異常是令人困惑的。其他顏色......無論如何,我離題了。 關鍵是,如果你想實現它,你必須只在一個地方做到這一點,而不必復制16次。
你可以抽象送人有點進一步作出這樣的規則單獨的對象,你可以把一個列表或字典,但像這樣的定義,它不使其成為更具可讀性,但它確實增加了一些可驗證性的好處,以及添加不同類型規則的可能性,而不會再次弄亂您的清潔列表。
我認為你最好的選擇是停止一個將你的值映射到方法的Dictionary
。
現在有幾個選項可能是你詞典中的關鍵 - 見下文。
免責聲明請注意,if if語句只是一個問題,如果它在代碼庫中傳播/復制,並且頻繁更改 - 將內容放入字典並不能真正降低復雜性 。 在字典中使用方法也會改變代碼的語義。 第一個問題應該是 - 我是否在運行時更改映射? 它們真的應該是動態的嗎?
ValueTuple
結構的Dictionary
可以使用語法(Shape, Color, Material)
- 這是最簡單的一種。 請注意 - 不是 Tuple
類,而是ValueTuple
結構。 Dictionary
與關鍵Item
類本身,但那么你需要在Item
注意正確的相等比較 。 您可以使Item
成為一個免費獲取的結構 (但性能較慢,來自System.ValueType
中通常情況下使用反射的相等比較),或者將其保留為類(或結構)並實現IEquatable<Item>
, Equals
和GetHashCode
。 ValueTuple
來簡化您的代碼。 AnyBlueGlassHandler : Handler
。 每個處理程序檢查If
條件,當為true時,運行操作。 然后,您可以將處理程序放在List<T>
,並將它們應用於像handlers.Foreach(x=>x.Handle(item))
Item
是鍵的代碼可能如下所示:
public static class ItemExtensions
{
static Dictionary<Item, Action<Item>>
methods = new Dictionary<Item, Action<Item>>()
{
{ new Item(Shape.any, Color.blue, Material.glass), x=> { /*do something*/ } }
};
public static bool TryApply(this Item item)
{
if (methods.TryGetValue(item, out var action))
{
action(item);
return true;
}
return false;
}
}
ValueTuple
是鍵的代碼可能是這樣的
public static class ItemExtensionsUsingValueTuple
{
static Dictionary<(Shape, Color, Material), Action<Item>>
methods = new Dictionary<(Shape, Color, Material), Action<Item>>()
{
{ (Shape.any, Color.blue, Material.glass), x=> { /*do something*/ } }
};
public static bool TryApply(this Item item)
{
if (methods.TryGetValue((item.ItemShape, item.ItemColor, item.ItemMaterial), out var action))
{
action(item);
return true;
}
return false;
}
}
使用ifs的代碼的更精簡版本可能如下所示:
public (Shape, Color, Material) Key => (ItemShape, ItemColor, ItemMaterial);
if ( item.Key == (Shape.any, Color.blue, Material.glass)) { }
嘗試使用Dictionary<T>
,例如:
static Dictionary<Tuple<Shape, Color, Material>, Action> s_Actions = new
Dictionary<Tuple<Shape, Color, Material>, Action>() {
{Tuple.Create(Shape.square, Color.blue, Material.glass), () => { ... } },
...
{Tuple.Create(Shape.any, Color.red, Material.metal), () => { ... } },
...
};
private static void RunAction(MyItem item) {
Action action;
// Either exact match or match with Any
if (!s_Actions.TryGetValue(Tuple.Create(item.ItemShape, item.ItemColor, item.ItemMaterial),
out action))
action = s_Actions.FirstOrDefault(pair => pair.Key.Item1 == Color.any &&
pair.Key.Item2 == item.ItemColor &&
pair.Key.Item3 == item.ItemMaterial)
// Execute action if it's found
if (action != null)
action();
}
void RunAction((Shape shape, Color color, Material material) item)
{
switch(item)
{
case var i1 when i1.color == Color.red && i1.shape == Shape.square:
case var i2 when i2.color == Color.blue:
// Do action...
break;
case var i1 when i1.shape == Shape.trangle && i1.material == Material.metal:
// Do action...
break;
default:
// Do action...
break;
}
}
此解決方案使用Value Tuples ,但主要在switch語句中使用C#7 模式匹配 。
我不認為它完全解決了你的問題干凈,但我認為多行案例給你提供了超過if語句方法的額外可讀性,並且很容易實現。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.