【发布时间】:2016-02-18 13:33:37
【问题描述】:
如何将函数参数限制为某些特定的枚举?最好在编译时检查它(尽管我怀疑这是可能的)。
我有 2 个枚举(KeyCode 用于键盘键,Mouse.Button 用于鼠标按钮),它们在代码中的处理方式完全相同。我可以简单地重载函数并复制粘贴内容,但是,我想避免噩梦。
我目前拥有的简化版(在课堂内)
enum E1 { Zero, One, Two }
enum E2 { Three, Four, Five }
// Overloads so users can only use this with enums only of type E1 or E2
public void DoEnumStuff(E1 e) {
DoEnumStuffTemplate(e);
}
public void DoEnumStuff(E2 e) {
DoEnumStuffTemplate(e);
}
// private function so users cannot access this generic one
private void DoEnumStuffTemplate<T>(T e) where T : struct, IConvertible {
// check type for safety
if (!typeof(T).IsEnum || typeof(T).Name != "E1" || typeof(T).Name != "E2")
throw new ArgumentException();
// do lots of stuff
DoSomething(e); //<- overloaded function, accepts only E1 and E2 =ERROR
// do lots of other stuff
}
为了完整起见:
-
DoSomething的行为完全不同,具体取决于给定的类型 -
DoSomething在函数中被多次调用 - 我无法更改枚举
- 我不想改变
DoSomething
我想我需要能够告诉编译器通用的T 肯定是E1 或E2,但我不知道该怎么做。
编辑:情况
很多好的建议,但没有包含我想要的一切。我将在这里添加我目前需要的代码,希望能更清楚地解决这个问题。
我正在制作一个扫雷克隆来试用 Unity 2D。我基于与SFML 一起使用的C++ 库Thor 中的thor::ActionMap 类创建了一个Action 类。它只是允许简洁的代码,例如(在 C++ 中)
ActionMap actionMap<string>;
actionMap["Fire"] = Action(Keyboard::LeftControl) || Action(Mouse::Left);
// stuff
while (game.IsRunning()) {
if (actionMap["Fire"].IsActive()) //true if left control or left mouse button is held
// FIRE
// probably more stuff
}
其中ActionMap 只是一个键(这里是string)和Action 的字典。如您所见,Action 接受键盘和鼠标按钮,它们是 2 个不同的enums。因此相当于示例代码中的DoSomething(e)。
我现在正在创建一种可以一致地更改控件的方法。它使用enumEControls 作为键而不是string。这里KeyCode 包含所有键盘键和Mouse.Button 所有鼠标按钮。我需要在这里区分按下和释放按钮,这就是为什么EControls.TilePressed 和EControls.TileReleased 将具有相同的键并且需要区别对待例如EControls.GameEscape。这段代码又是在 C# 中。
private ActionMap _controls = new ActionMap<EControls>();
// Set controls for a keyboard key
public void SetControl(EControls control, KeyCode key) {
switch (control) {
// If either TilePressed or Released was given, set them both to the same key
case EControls.TilePressed:
case EControls.TileReleased:
//Here Action(...) is DoSomething(...) from the example code
_controls[EControls.TilePressed] = new Action(key, Action.EActionType.PressOnce);
_controls[EControls.TileReleased] = new Action(key, Action.EActionType.ReleaseOnce);
break;
case EControls.TileFlagPressed:
case EControls.TileFlagReleased:
_controls[EControls.TileFlagPressed] = new Action(key, Action.EActionType.PressOnce);
_controls[EControls.TileFlagReleased] = new Action(key, Action.EActionType.ReleaseOnce);
break;
case EControls.GameEscape:
_controls[EControls.GameEscape] = new Action(key, Action.EActionType.ReleaseOnce);
break;
default:
throw new ArgumentOutOfRangeException("control");
}
}
// Set controls for a mouse button
public void SetControl(EControls control, Mouse.Button button) {
// copy-pasted code :(
case EControls.TilePressed:
case EControls.TileReleased:
_controls[EControls.TilePressed] = new Action(button, Action.EActionType.PressOnce);
_controls[EControls.TileReleased] = new Action(button, Action.EActionType.ReleaseOnce);
break;
case EControls.TileFlagPressed:
case EControls.TileFlagReleased:
_controls[EControls.TileFlagPressed] = new Action(button, Action.EActionType.PressOnce);
_controls[EControls.TileFlagReleased] = new Action(button, Action.EActionType.ReleaseOnce);
break;
case EControls.GameEscape:
_controls[EControls.GameEscape] = new Action(button, Action.EActionType.ReleaseOnce);
break;
default:
throw new ArgumentOutOfRangeException("control");
}
}
如您所见,几乎每一行代码中都存在new Action(...),而if (typeof(T).GetType() == typeof(E1)) 之类的代码基本上与复制粘贴函数的内容相同。这是我想避免的事情(复制粘贴在编译时甚至会更安全)。但就目前而言,这似乎是不可能的。
由于在更大的游戏中您可能会定期添加一些新控件,这会很烦人。
对不起,文字墙:s
【问题讨论】:
-
我认为重载没问题....这就是它专门设计的..为什么你认为它不好?
-
如果
DoSomething对不同类型的行为不同,为什么要强制将事情变成一个方法?只需重载并使用两个单独的方法。 -
"I'd like to avoid the nightmares"- 噩梦,例如......什么,究竟是什么?方法重载是该语言的一个非常简单的部分。重复的关注点可以很容易地重构。 -
您应该考虑将 != "E1" 替换为 typeof(E1) 等。另外我建议在接收 int 的私有方法中做所有常见的事情,并在 DoSomething 的每个重载中调用它(e ) 并完全删除 DoEnumStuffTemplate
-
我不想要复制意大利面,因为该功能很长并且需要定期更新。这是我可以做噩梦的复制粘贴,而不是方法重载。
标签: c# enums type-safety