我正在使用C#编写PLC语言解释器。该PLC语言包含20多种数据类型和25种左右的指令。一旦我开始生成代码,我就会平衡两种不同的写入指令的方式:
1)每种指令都在一个包含大switch
的类中表示,以便选择数据类型。例如:
public class ADD : Instruction
{
private string type;
public ADD(string type)
{
this.type = type;
}
public bool Exec(Context c)
{
switch (type)
{
case "INT":
short valor2 = c.PopINT();
short valor = c.PopINT();
short result = (short)(valor + valor2);
c.PushINT(result);
break;
case "DINT":
int valor4 = c.PopDINT();
int valor3 = c.PopDINT();
int result2 = (int)(valor4 + valor3);
c.PushDINT(result2);
break;
case "BOOL":
// Implement BOOL
break;
// Implement other types...
default:
break;
}
c.IP++;
return false; ;
}
}
2)每个类代表一个具有单一数据类型的指令。这样可以避免大switch
。例如:
public class ADDi : Instruction
{
public bool Exec(Context c)
{
short valor = c.PopINT();
short valor2 = c.PopINT();
short result = (short)(valor + valor2);
c.PushINT(result);
c.IP++;
return false;
}
}
我正在使用COMMAND设计模式(Exec()
)来编写指令。我认为第二种选择更好,因为避免了大转换,但这种选择涉及写入超过400条指令。
始终记住,在这种情况下,执行性能比翻译中的性能更重要。
所以,我的确切问题如下:是否有其他方法来分解指令和数据类型?我正在寻找少量的指令而不会影响性能。
编辑:
此图显示了我的类型层次结构:
这是INT类实现:
public class INT : ANY_INT
{
public override string DefaultInitValue()
{
return "0";
}
public override int GetBytes()
{
return 2;
}
public override string GetLastType()
{
return this.ToString();
}
public override string ToString()
{
return "INT";
}
}
有些类更复杂(结构,数组,......)。
操作Push和Pop定义如下:
public void PushINT(short value)
{
//SP -> Stack Pointer
resMem.WriteINT(SP, value);
SP += 2;
}
public short PopINT()
{
SP -= 2;
short value = resMem.ReadINT(SP);
return value;
}
最后,在内存中进行读写操作。
public void WriteINT(int index, short entero)
{
SetCapacity(index + 2); // Memory grows up dinamically
memory[index] = (sbyte)((ushort)entero >> 8 & 0x00FF);
memory[index + 1] = (sbyte)((ushort)entero >> 0 & 0x00FF);
}
public short ReadINT(int index)
{
return (short)(((short)(memory[index]) << 8 & 0xFF00) |
((short)(memory[index + 1]) & 0x00FF));
}
我希望此信息有所帮助。谢谢。
答案 0 :(得分:7)
如果您可以更改Context的实现以支持泛型类型(例如,Pop<int>
而不是PopINT()
),则可以使用委托来简化实现。
增加:
var addInt = new MathInstruction<int>((a, b) => a + b));
var addDouble = new MathInstruction<double>((a, b) => a + b));
var addDecimal = new MathInstruction<decimal>((a, b) => a + b));
减法:
var subtractInt = new MathInstruction<int>((a, b) => a - b));
var subtractDouble = new MathInstruction<double>((a, b) => a - b));
var subtractDecimal = new MathInstruction<decimal>((a, b) => a - b));
司:
var divideIntAsDouble = new MathInstruction<int, double>((a, b) => a / b));
var divideDouble = new MathInstruction<double>((a, b) => a / b));
var divideDecimal = new MathInstruction<decimal>((a, b) => a / b));
类型之间的转换:
var addIntAndDouble = new MathInstruction<int, double, double>((a, b) => a + b));
它将实现如下:
class MathInstruction<TA, TB, TResult> : Instruction
{
private Func<TA, TB, TResult> callback;
public MathInstruction(Func<TA, TB, TResult> callback)
{
this.callback = callback;
}
public bool Exec(Context c)
{
var a = c.Pop<TA>();
var b = c.Pop<TB>();
var result = callback(a, b);
c.Push<TResult>(result);
return false;
}
}
// Convenience
class MathInstruction<T, TResult> : MathInstruction<T, T, TResult>
class MathInstruction<T> : MathInstruction<T, T, T>
我想象你的上下文只有Stack<object>
和PopINT
,PopBOOL
等,只需弹出参数并进行投射。在这种情况下,您可以使用:
public T Pop<T>()
{
var o = stack.Pop();
return Convert.ChangeType(o, typeof(T));
}
public void Push<T>(T item)
{
stack.Push(item);
}
请注意,这也可以处理您的逻辑运算符 - 例如:
var logicalAnd = new MathInstruction<bool>((a, b) => a && b);
var logicalOr = new MathInstruction<bool>((a, b) => a || b);
答案 1 :(得分:2)
你能使用继承吗?我会看到有关数据类型的继承的巧妙组合,然后是将执行委托给适当对象的策略模式。
但是我们真的需要看一个类的图表来帮助你。
只记得编程接口而不是类型,而且,组合比继承更强大。我希望这可以帮助你。