12

Attualmente sto affrontando un nuovo problema con gli operatori. Usando il seguente codice, voglio fare un output che sarebbe lo stesso di quando si usa la coppia if ... else in C#.Riflessione C#: se ... altro?

var method = new DynamicMethod("dummy", null, Type.EmptyTypes); 
var g = method.GetILGenerator(); 

g.Emit(OpCodes.Ldstr, "string"); 
g.Emit(OpCodes.Ldstr, "string"); 
g.Emit(OpCodes.Call, typeof(String).GetMethod("op_Equality", new Type[]{typeof(string), typeof(string)})); 
g.Emit(OpCodes.Ldc_I4, 0); 
g.Emit(OpCodes.Ceq); 
g.Emit(OpCodes.Brtrue_S,); 

var action = (Action)method.CreateDelegate(typeof(Action)); 
action(); 

Console.Read(); 

Le mie domande sono:

  1. Come posso ottenere l'indirizzo di un'istruzione di passare come parametro per i codici operativi di filiale?
  2. C'è qualche differenza tra il BR e BR_S, Brtrue e Brtrue_S, Brfalse e Brfalse_S e le istruzioni simili?

Grazie.

+3

Come altri hanno già menzionato, le versioni "_S" delle istruzioni derivano un offset di 1 byte anziché di 4 byte. Se sai che il tuo ramo sarà sempre all'interno dell'intervallo disponibile (da -128 byte a +127 byte), otterrai un codice più compatto usandoli, tuttavia se proverai a utilizzarli per passare a un'etichetta con un offset esterno questo intervallo, verrà generata un'eccezione durante la creazione del delegato. – Iridium

risposta

8

ILGenerator.ILOffset fornisce l'offset corrente nel flusso IL, se è ciò che si desidera. È possibile utilizzare anche DefineLabel e MarkLabel, come suggerito da goric.

L'unica differenza tra brtrue.s e brtrue è che brtrue.s è la versione breve di brtrue. brtrue utilizza uno scostamento di 4 byte e brtrue.s utilizza uno scostamento di 1 byte. Lo stesso vale per brfalse e brfalse.s (e br/br.s).

Queste non sono le uniche versioni brevi di un'istruzione IL, ci sono anche altre brevi istruzioni, come ldc.i4.0 - ldc.i4.8 per il caricamento di numeri interi. Questi sono principalmente utili per generare file binari più piccoli, ma non credo che ci sia una grande differenza altrimenti.

8
  1. È possibile utilizzare una combinazione dei metodi DefineLabel e MarkLabel per determinare la posizione di destinazione del ramo . Per fare ciò, dichiara le etichette di cui avrai bisogno, ad esempio equal e notequal. Quindi puoi contrassegnare i punti nel tuo IL dove devono essere presenti le etichette. Una volta eseguita questa operazione, è possibile impostare l'obiettivo dell'istruzione derivata su questa etichetta.

    // Define labels 
    Label equal = g.DefineLabel(); 
    Label notEqual = g.DefineLabel(); 
    Label endOfMethod = g.DefineLabel(); 
    // your logic here 
    g.Emit(OpCodes.Brtrue_S, equal); 
    g.MarkLabel(equal); 
    // some code if they are equal 
    g.MarkLabel(notEqual); 
    // some code if they are not equal 
    g.MarkLabel(endOfMethod); // this marks the return point 
    g.Emit(OpCodes.Ret); 
    
  2. La differenza tra Br, Brtrue e Brfalse e le loro controparti _S è la lunghezza del salto. Lo _S indica una forma abbreviata; l'istruzione di destinazione è un offset firmato a 1 byte dall'istruzione successiva. Nel formato standard (non breve), l'obiettivo è rappresentato da uno scostamento di 4 byte.

4

Questo è nuovo per me, ma da un po 'di ricerca a quanto pare si ottiene l'indirizzo chiamando

Label targetInstruction = g.DefineLabel(); 

ad un certo punto in anticipo (per esempio prima del primo Emit?), E poi

g.MarkLabel(targetInstruction); 

poco prima che emette la linea che si desidera diramare -. Quindi questo Label è l'argomento dell'opcode Br____.

La differenza tra Br, Brtrue e Brfalse è che Br si dirama sempre e gli altri due inseriscono un valore dallo stack e si diramano solo se è rispettivamente vero o falso. (Quindi sì, c'è una grande differenza!) Il _S significa "forma breve", ma non sono sicuro di cosa significhi.

Problemi correlati