2

C# を使用して PLC 言語インタープリターを作成しています。その PLC 言語には、20 を超えるデータ タイプと 25 程度の命令が含まれています。コードの生成を開始するとすぐに、命令を記述する 2 つの異なる方法のバランスを取ります。

1) あらゆる種類の命令はswitch、データ型を選択するために big を含む 1 つのクラスで表されます。例:

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 desing pattern ( Exec()) を使用して命令を記述しています。大きなスイッチを回避するため、2 番目の選択肢の方が優れていると思いますが、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";
    }

}

一部のクラスはより複雑です (構造体、配列など)。

操作のプッシュとポップは次のように定義されます。

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));
}

この情報がお役に立てば幸いです。ありがとうございました。

4

2 に答える 2

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>andPopINTなどが含まれていると想像しています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);
于 2012-03-29T12:11:33.257 に答える
2

継承を使用できますか?データ型に関する継承の巧妙な組み合わせと、実行を適切なオブジェクトに委譲する戦略パターンが見られます。

しかし、その場合は、クラス図を見て助けてもらう必要があります。

型ではなくインターフェイスにプログラミングすることを忘れないでください。また、合成は継承よりも強力です。これがあなたを助けることができることを願っています。

于 2012-03-29T11:33:46.997 に答える