如何在没有多个 if 语句的情况下检查条件?
How to check conditions without multiple if statements?
我正在 Xamarin 中为 android 开发游戏,但我有两个特别枯燥的部分(不要重复自己)。
第一部分是当我想改变配乐(音频播放)或背景时,我根据级别为同一配乐设置了三个不同的音高,对于背景 canvas 交替的背景也是如此.
对于这些方法,条件基于等于玩家所在级别的整数级别。
示例代码
private void SetBackgrounds()
{
if (level == 5)
{
gameAreaCanvas.SetBackgroundResource(Resource.Drawable.LevelUpOneBackground);
}
else if (level == 10)
{
gameAreaCanvas.SetBackgroundResource(Resource.Drawable.LevelUpTwoBackground);
}
else if (level == 15)
{
gameAreaCanvas.SetBackgroundResource(Resource.Drawable.LevelUpThreeBackground);
}
}
代码的不同部分也是如此,其中很多基于整数值,即级别。每当玩家前进时,级别整数递增 1,然后 activity 具有检查整数级别的方法。代码可以工作,但显然效率很低,因为有很多重复的代码,稍作调整。
例如关卡是这样的。
if(level == 1) {
levelDisplay.Text = "LEVEL 1";
timer = new Timer();
timer.Interval = 2000; /// DIFFERENT
timer.Enabled = true;
timer.Elapsed += Level1; /// DIFFERENT
timer.Start();
}
///LEVEL 2
if (level == 2)
{
levelDisplay.Text = "LEVEL 2";
timer = new Timer();
timer.Interval = 2000; /// DIFFERENT
timer.Enabled = true;
timer.Elapsed += Level2; /// DIFFERENT
timer.Start();
}
有没有办法让这段代码不那么干?感谢输入。
它们被称为 switch 语句,它们看起来像这样:
switch(level)
{
case 1:
doLevel1();
break;
case 2:
doLevel2();
break;
}
可在此处找到更多信息:Switch Statement Tutorial
您是否在寻找词典?
//TODO: please, check dictionary's value type
private static Dictionary<int, Resource.Drawable> s_Backgrounds =
new Dictionary<int, Resource.Drawable>() {
{5, Resource.Drawable.LevelUpOneBackground},
{10, Resource.Drawable.LevelUpTwoBackground},
{15, Resource.Drawable.LevelUpThreeBackground},
};
...
private void SetBackgrounds() {
gameAreaCanvas.SetBackgroundResource(s_Backgrounds[level]);
}
编辑: 与 level
相同;唯一的区别是您有 三个 值对应于每个键。最简单的解决方案是将这些值组织成 Tuple
(不过,自定义 class 将是更好的选择):
// I've used Tuple<string, int, int> to store three values
// you may want to change it to a custom class
private static Dictionary<int, Tuple<string, int, int>> s_Levels =
new Dictionary<int, Tuple<string, int, int>>() {
{1, new Tuple<string, int, int>("LEVEL 1", 2000, Level1)},
{2, new Tuple<string, int, int>("LEVEL 2", 2000, Level2)},
};
...
levelDisplay.Text = s_Levels[level].Item1;
timer = new Timer();
timer.Interval = s_Levels[level].Item2; /// DIFFERENT
timer.Enabled = true;
timer.Elapsed += s_Levels[level].Item3; /// DIFFERENT
imer.Start();
第一部分可以压缩如下:
canvas.SetBackgroundResource( level == 5? Resource.Drawable.LevelUpOneBackground :
level = 10? Resource.Drawable.LevelUpTwoBackground :
Resource.Drawable.LevelUpThreeBackground );
或者,更好的是,创建一个字典,将关卡编号映射到背景,这样你就可以拥有:
gameAreaCanvas.SetBackgroundResource( resourcesFromLevels[level] );
简化第二部分有点复杂。
在这种情况下,一种可能的解决方案是继承。
您制作了一个新的摘要 class Level
来代表您的游戏关卡,并为每个特定关卡创建了该 class 的子class。因此,您将有 class Level1: Level
、class Level2: Level
等。基础 class 有一个 Setup()
方法,它通过调用自身的可重写来工作,每个可重写都有一个默认实现,但是 Level
的后代可以提供他们自己的实现。
此外,并非所有事情都必须由可覆盖对象处理。 Level
class 可以接受一些构造函数参数,比如关卡名称,然后每个后代都可以向基础 class 提供正确的关卡名称。所以,它看起来像这样:
class Level
{
readonly string levelName;
Level( String levelName )
{
this.levelName = levelName;
}
void Setup()
{
levelDisplay.Text = levelName;
SetupTimer();
}
virtual void SetupTimer()
{
//Default implementation
}
}
class Level1: Level
{
Level1() : Level( "LEVEL 1" )
{
}
override void SetupTimer()
{
//Level1 implementation
}
}
我会让你的 "levels" 更面向对象:
public LevelObject
{
public BackgroundResource BackgroundResource { get; set; }
public string Text { get; set; }
public double TimeInterval { get; set; }
public double ElapsedInterval { get; set; }
// constructors, methods, etc...
}
这样,您可以初始化 "levels" 的集合,例如 List<LevelObject>
,然后根据您当前的 level
,根据需要设置任何属性:
int level = // current level
List<LevelObject> levelObjectList =
new List<LevelObject>
{
new LevelObject("LEVEL 1", 2000, Level1),
new LevelObject("LEVEL 2", 2000, Level2),
// etc...
}
LevelObject levelObject = levelObjectList[level];
示例:
private void SetBackgrounds(LevelObject levelObject)
{
if (levelObject.BackgroundResource != null)
{
gameAreaCanvas.SetBackgroundResource(levelObject.BackgroundResource);
}
}
levelDisplay.Text = levelObject.Text;
timer = new Timer();
timer.Interval = levelObject.TimeInterval;
timer.Enabled = true;
timer.Elapsed += levelObject.ElapsedInterval;
timer.Start();
我会走这条路:
首先,你需要一种枚举
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ConsoleApp1
{
public class MyEnumeration
{
#region Private Fields
private readonly string _displayName;
private readonly int _value;
private readonly int _interval;
private readonly Action _action;
#endregion Private Fields
#region Protected Constructors
protected MyEnumeration()
{
}
protected MyEnumeration(int value, string displayName, int interval, Action action)
{
_value = value;
_displayName = displayName;
_interval = interval;
_action = action;
}
#endregion Protected Constructors
#region Public Properties
public string DisplayName
{
get { return _displayName; }
}
public int Value
{
get { return _value; }
}
public int Interval
{
get { return _interval; }
}
public Action Action
{
get { return _action; }
}
#endregion Public Properties
#region Public Methods
public static int AbsoluteDifference(MyEnumeration firstValue, MyEnumeration secondValue)
{
var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
return absoluteDifference;
}
public static T FromDisplayName<T>(string displayName) where T : MyEnumeration, new()
{
var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
return matchingItem;
}
public static T FromValue<T>(int value) where T : MyEnumeration, new()
{
var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
return matchingItem;
}
public static IEnumerable<T> GetAll<T>() where T : MyEnumeration, new()
{
var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (var info in fields)
{
var instance = new T();
var locatedValue = info.GetValue(instance) as T;
if (locatedValue != null)
{
yield return locatedValue;
}
}
}
public int CompareTo(object other)
{
return Value.CompareTo(((MyEnumeration)other).Value);
}
public override bool Equals(object obj)
{
var otherValue = obj as MyEnumeration;
if (otherValue == null)
{
return false;
}
var typeMatches = GetType().Equals(obj.GetType());
var valueMatches = _value.Equals(otherValue.Value);
return typeMatches && valueMatches;
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public override string ToString()
{
return DisplayName;
}
#endregion Public Methods
#region Private Methods
private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : MyEnumeration, new()
{
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
if (matchingItem == null)
{
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
throw new ApplicationException(message);
}
return matchingItem;
}
#endregion Private Methods
}
}
然后您可以创建自己的枚举,例如:
using System;
namespace ConsoleApp1
{
internal class LevelEnum : MyEnumeration
{
public static readonly LevelEnum Level1 = new LevelEnum(1, "Level 1", 2000, Program.Level1);
public static readonly LevelEnum Level2 = new LevelEnum(2, "Level 2", 3000, Program.Level2);
public static readonly LevelEnum Level3 = new LevelEnum(3, "Level 3", 4000, Program.Level3);
public static readonly LevelEnum Level4 = new LevelEnum(4, "Level 4", 5000, Program.Level4);
public static readonly LevelEnum Level5 = new LevelEnum(5, "Level 5", 6000, Program.Level5);
public static readonly LevelEnum Level6 = new LevelEnum(6, "Level 6", 7000, Program.Level6);
public static readonly LevelEnum Level7 = new LevelEnum(7, "Level 7", 8000, Program.Level7);
public static readonly LevelEnum Level8 = new LevelEnum(8, "Level 8", 9000, Program.Level8);
public LevelEnum()
{
}
protected LevelEnum(int value, string displayName, int interval, Action action) : base(value, displayName, interval, action)
{
}
}
}
您可以像这样使用它:
private static void Main(string[] args)
{
int level = 5;
LevelEnum levelEnum = MyEnumeration.FromValue<LevelEnum>(level);
levelDisplay.Text = levelEnum.DisplayName;
timer = new Timer();
timer.Interval = levelEnum.Interval;
timer.Enabled = true;
timer.Elapsed += levelEnum.Action;
timer.Start();
}
internal static void Level1()
{
// Action for Level 1
}
internal static void Level2()
{
// Action for Level 2
}
internal static void Level3()
{
// Action for Level 3
}
我正在 Xamarin 中为 android 开发游戏,但我有两个特别枯燥的部分(不要重复自己)。
第一部分是当我想改变配乐(音频播放)或背景时,我根据级别为同一配乐设置了三个不同的音高,对于背景 canvas 交替的背景也是如此.
对于这些方法,条件基于等于玩家所在级别的整数级别。
示例代码
private void SetBackgrounds()
{
if (level == 5)
{
gameAreaCanvas.SetBackgroundResource(Resource.Drawable.LevelUpOneBackground);
}
else if (level == 10)
{
gameAreaCanvas.SetBackgroundResource(Resource.Drawable.LevelUpTwoBackground);
}
else if (level == 15)
{
gameAreaCanvas.SetBackgroundResource(Resource.Drawable.LevelUpThreeBackground);
}
}
代码的不同部分也是如此,其中很多基于整数值,即级别。每当玩家前进时,级别整数递增 1,然后 activity 具有检查整数级别的方法。代码可以工作,但显然效率很低,因为有很多重复的代码,稍作调整。
例如关卡是这样的。
if(level == 1) {
levelDisplay.Text = "LEVEL 1";
timer = new Timer();
timer.Interval = 2000; /// DIFFERENT
timer.Enabled = true;
timer.Elapsed += Level1; /// DIFFERENT
timer.Start();
}
///LEVEL 2
if (level == 2)
{
levelDisplay.Text = "LEVEL 2";
timer = new Timer();
timer.Interval = 2000; /// DIFFERENT
timer.Enabled = true;
timer.Elapsed += Level2; /// DIFFERENT
timer.Start();
}
有没有办法让这段代码不那么干?感谢输入。
它们被称为 switch 语句,它们看起来像这样:
switch(level)
{
case 1:
doLevel1();
break;
case 2:
doLevel2();
break;
}
可在此处找到更多信息:Switch Statement Tutorial
您是否在寻找词典?
//TODO: please, check dictionary's value type
private static Dictionary<int, Resource.Drawable> s_Backgrounds =
new Dictionary<int, Resource.Drawable>() {
{5, Resource.Drawable.LevelUpOneBackground},
{10, Resource.Drawable.LevelUpTwoBackground},
{15, Resource.Drawable.LevelUpThreeBackground},
};
...
private void SetBackgrounds() {
gameAreaCanvas.SetBackgroundResource(s_Backgrounds[level]);
}
编辑: 与 level
相同;唯一的区别是您有 三个 值对应于每个键。最简单的解决方案是将这些值组织成 Tuple
(不过,自定义 class 将是更好的选择):
// I've used Tuple<string, int, int> to store three values
// you may want to change it to a custom class
private static Dictionary<int, Tuple<string, int, int>> s_Levels =
new Dictionary<int, Tuple<string, int, int>>() {
{1, new Tuple<string, int, int>("LEVEL 1", 2000, Level1)},
{2, new Tuple<string, int, int>("LEVEL 2", 2000, Level2)},
};
...
levelDisplay.Text = s_Levels[level].Item1;
timer = new Timer();
timer.Interval = s_Levels[level].Item2; /// DIFFERENT
timer.Enabled = true;
timer.Elapsed += s_Levels[level].Item3; /// DIFFERENT
imer.Start();
第一部分可以压缩如下:
canvas.SetBackgroundResource( level == 5? Resource.Drawable.LevelUpOneBackground :
level = 10? Resource.Drawable.LevelUpTwoBackground :
Resource.Drawable.LevelUpThreeBackground );
或者,更好的是,创建一个字典,将关卡编号映射到背景,这样你就可以拥有:
gameAreaCanvas.SetBackgroundResource( resourcesFromLevels[level] );
简化第二部分有点复杂。
在这种情况下,一种可能的解决方案是继承。
您制作了一个新的摘要 class Level
来代表您的游戏关卡,并为每个特定关卡创建了该 class 的子class。因此,您将有 class Level1: Level
、class Level2: Level
等。基础 class 有一个 Setup()
方法,它通过调用自身的可重写来工作,每个可重写都有一个默认实现,但是 Level
的后代可以提供他们自己的实现。
此外,并非所有事情都必须由可覆盖对象处理。 Level
class 可以接受一些构造函数参数,比如关卡名称,然后每个后代都可以向基础 class 提供正确的关卡名称。所以,它看起来像这样:
class Level
{
readonly string levelName;
Level( String levelName )
{
this.levelName = levelName;
}
void Setup()
{
levelDisplay.Text = levelName;
SetupTimer();
}
virtual void SetupTimer()
{
//Default implementation
}
}
class Level1: Level
{
Level1() : Level( "LEVEL 1" )
{
}
override void SetupTimer()
{
//Level1 implementation
}
}
我会让你的 "levels" 更面向对象:
public LevelObject
{
public BackgroundResource BackgroundResource { get; set; }
public string Text { get; set; }
public double TimeInterval { get; set; }
public double ElapsedInterval { get; set; }
// constructors, methods, etc...
}
这样,您可以初始化 "levels" 的集合,例如 List<LevelObject>
,然后根据您当前的 level
,根据需要设置任何属性:
int level = // current level
List<LevelObject> levelObjectList =
new List<LevelObject>
{
new LevelObject("LEVEL 1", 2000, Level1),
new LevelObject("LEVEL 2", 2000, Level2),
// etc...
}
LevelObject levelObject = levelObjectList[level];
示例:
private void SetBackgrounds(LevelObject levelObject)
{
if (levelObject.BackgroundResource != null)
{
gameAreaCanvas.SetBackgroundResource(levelObject.BackgroundResource);
}
}
levelDisplay.Text = levelObject.Text;
timer = new Timer();
timer.Interval = levelObject.TimeInterval;
timer.Enabled = true;
timer.Elapsed += levelObject.ElapsedInterval;
timer.Start();
我会走这条路:
首先,你需要一种枚举
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ConsoleApp1
{
public class MyEnumeration
{
#region Private Fields
private readonly string _displayName;
private readonly int _value;
private readonly int _interval;
private readonly Action _action;
#endregion Private Fields
#region Protected Constructors
protected MyEnumeration()
{
}
protected MyEnumeration(int value, string displayName, int interval, Action action)
{
_value = value;
_displayName = displayName;
_interval = interval;
_action = action;
}
#endregion Protected Constructors
#region Public Properties
public string DisplayName
{
get { return _displayName; }
}
public int Value
{
get { return _value; }
}
public int Interval
{
get { return _interval; }
}
public Action Action
{
get { return _action; }
}
#endregion Public Properties
#region Public Methods
public static int AbsoluteDifference(MyEnumeration firstValue, MyEnumeration secondValue)
{
var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
return absoluteDifference;
}
public static T FromDisplayName<T>(string displayName) where T : MyEnumeration, new()
{
var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
return matchingItem;
}
public static T FromValue<T>(int value) where T : MyEnumeration, new()
{
var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
return matchingItem;
}
public static IEnumerable<T> GetAll<T>() where T : MyEnumeration, new()
{
var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (var info in fields)
{
var instance = new T();
var locatedValue = info.GetValue(instance) as T;
if (locatedValue != null)
{
yield return locatedValue;
}
}
}
public int CompareTo(object other)
{
return Value.CompareTo(((MyEnumeration)other).Value);
}
public override bool Equals(object obj)
{
var otherValue = obj as MyEnumeration;
if (otherValue == null)
{
return false;
}
var typeMatches = GetType().Equals(obj.GetType());
var valueMatches = _value.Equals(otherValue.Value);
return typeMatches && valueMatches;
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public override string ToString()
{
return DisplayName;
}
#endregion Public Methods
#region Private Methods
private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : MyEnumeration, new()
{
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
if (matchingItem == null)
{
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
throw new ApplicationException(message);
}
return matchingItem;
}
#endregion Private Methods
}
}
然后您可以创建自己的枚举,例如:
using System;
namespace ConsoleApp1
{
internal class LevelEnum : MyEnumeration
{
public static readonly LevelEnum Level1 = new LevelEnum(1, "Level 1", 2000, Program.Level1);
public static readonly LevelEnum Level2 = new LevelEnum(2, "Level 2", 3000, Program.Level2);
public static readonly LevelEnum Level3 = new LevelEnum(3, "Level 3", 4000, Program.Level3);
public static readonly LevelEnum Level4 = new LevelEnum(4, "Level 4", 5000, Program.Level4);
public static readonly LevelEnum Level5 = new LevelEnum(5, "Level 5", 6000, Program.Level5);
public static readonly LevelEnum Level6 = new LevelEnum(6, "Level 6", 7000, Program.Level6);
public static readonly LevelEnum Level7 = new LevelEnum(7, "Level 7", 8000, Program.Level7);
public static readonly LevelEnum Level8 = new LevelEnum(8, "Level 8", 9000, Program.Level8);
public LevelEnum()
{
}
protected LevelEnum(int value, string displayName, int interval, Action action) : base(value, displayName, interval, action)
{
}
}
}
您可以像这样使用它:
private static void Main(string[] args)
{
int level = 5;
LevelEnum levelEnum = MyEnumeration.FromValue<LevelEnum>(level);
levelDisplay.Text = levelEnum.DisplayName;
timer = new Timer();
timer.Interval = levelEnum.Interval;
timer.Enabled = true;
timer.Elapsed += levelEnum.Action;
timer.Start();
}
internal static void Level1()
{
// Action for Level 1
}
internal static void Level2()
{
// Action for Level 2
}
internal static void Level3()
{
// Action for Level 3
}