我正在尝试为每种数据类型简化具有不同堆栈的基于堆栈的解释器。
基本上,我有一堆如下所示的指令:
class Interpreter {
Stack<int> IntStack;
Stack<float> FloatStack;
Stack<char> CharStack;
// ... Continued for various other types.
}
class PushInt : Instruction {
Interpreter interpreter;
int value;
void Execute() {
interpreter.IntStack.Push(value);
}
}
class PopInt : Instruction {
Interpreter interpreter;
void Execute() {
interpreter.IntStack.Pop();
}
}
对于PushFloat
,PopFloat
,PushChar
,PopChar
等,我有相同的看法
这让我大叫“泛型”,我想做的是改为定义Push<T>
和Pop<T>
。但是我在这里遇到了一个问题:
class Push<T> : Instruction {
Interpreter interpreter;
T value;
void Execute() {
interpreter.[What on earth goes here?].Push(value);
}
}
class Pop<T> : Instruction {
Interpreter interpreter;
void Execute() {
interpreter.[What on earth goes here?].Pop();
}
}
我认为,肯定有一种定义Interpreter.Push<T>(value)
的方法,但是我在该方法中遇到了同样的问题-我不确定如何根据泛型类型参数选择正确的堆栈不用一团糟。
我尝试过的一种解决方案是使用静态类:
public static class ArgumentStack<T> {
public static Stack<T> Stack;
public static void Push(T value) {
Stack.Push(value);
}
public static T Pop() {
return Stack.Pop();
}
}
public class Push<T> : Instruction {
T value;
public override void Execute() {
ArgumentStack<T>.Push(Value);
}
}
public class Pop<T> : Instruction {
public override void Execute() {
ArgumentStack<T>.Pop();
}
}
但是问题是,如果一次使用多个解释器,那么整个事情就会开始崩溃,因为每种堆栈类型只有一个实例存在,并且所有解释器都可以共享它们。
令人沮丧的是,静态方法几乎完全是我想要的用法,我只是希望我可以在每个Interpreter对象中而不是在类级别进行操作。
有什么想法吗?
答案 0 :(得分:0)
您是对的,泛型是此处的关键。似乎您只是希望一个类(解释器)在其中具有未知类型的Stack属性。从那里,您希望能够使用Push和Pop这样在堆栈中添加和删除项目:
public class Interpreter<T>
{
public Stack<T> TypeStack { get; set; }
public Interpreter()
{
if (this.TypeStack == null)
{
this.TypeStack = new Stack<T>();
}
}
public void Push(T value)
{
this.TypeStack.Push(value);
}
public void Pop()
{
this.TypeStack.Pop();
}
}
您将像这样使用它:
var interpreter = new Interpreter<int>();
interpreter.Push(5);
interpreter.Pop();
答案 1 :(得分:0)
我想出了一个我很满意的解决方案。但是,如果有人有更漂亮的东西,这个问题就可以解决。
我在静态通用类中定义了一个静态字典,该字典将解释器映射到各个堆栈,然后定义了一个解释器方法,该方法检索正确的通用堆栈。下面的代码示例:
public static class ArgumentStack<T> {
public static Dictionary<Interpreter, Stack<T>> Stacks;
}
public class Interpreter {
public Stack<T> GetStack<T>() {
return ArgumentStack<T>.Stacks[this];
}
}
public class Push<T> : Instruction {
Interpreter interpreter;
T value;
public override void Execute() {
interpreter.GetStack<T>().Push(Value);
}
}
public class Pop<T> : Instruction {
Interpreter interpreter;
public override void Execute() {
interpreter.GetStack<T>().Pop();
}
}
答案 2 :(得分:0)
这是您可能会适应的方法。看来您希望能够使用许多不同的类型,同时根据它们的类型将它们分别路由到自己的堆栈。 (很难理解您将要使用它的什么地方,但是我会留给您。)
public class Interpreter
{
private readonly Dictionary<Type, Stack> _stacksByType = new Dictionary<Type, Stack>();
public void Push<T>(T item)
{
if (!_stacksByType.ContainsKey(typeof(T)))
{
_stacksByType.Add(typeof(T), new Stack());
}
_stacksByType[typeof(T)].Push(item);
}
public T Pop<T>()
{
// If we pop an empty stack it throws an InvalidOperationException
// so I'm throwing the same exception if there's no corresponding stack.
if (!_stacksByType.ContainsKey(typeof(T)))
throw new InvalidOperationException("There is nothing in the stack.");
return (T)_stacksByType[typeof(T)].Pop();
}
}
这允许Interpreter
封装其所有各种堆栈。 Push
项时,它会根据其类型将其放入Stack
中。如果堆栈不存在,则会创建堆栈。
Pop
会根据类型从堆栈中弹出。如果该类型的堆栈不存在,它将抛出与堆栈为空时相同的异常。
因为我们通过控制项目进入哪个Stack
来管理类型安全,所以我们不需要通用的Stack<T>
。
这种封装还可以避免这种情况:
interpreter.IntStack.Push(value);
如果Interpreter
之外的类不知道它如何在内部管理所有这些,那就更好了。不必告诉类将值放入哪个堆栈,我们只需给它提供值并让其计算出来即可。