12

저는 현재 운영자에게 새로운 문제에 직면하고 있습니다. 다음 코드를 사용하여 C#에서 if ... else 쌍을 사용할 때와 동일한 출력을 만들고 싶습니다.C# reflection : If ... else?

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

내 질문은 :

  1. 하는 방법은 명령어의 주소가 지점 옵 코드에 대한 매개 변수로 전달받을 수 있나요?
  2. BRBR_S, BrtrueBrtrue_S, BrfalseBrfalse_S 및 이와 유사한 지침이 있습니까?

감사합니다.

+3

다른 언급했듯이 분기 명령어의 "_S"버전은 4 바이트 오프셋 대신 1 바이트를 사용합니다. 분기가 항상 사용할 수있는 범위 (-128 바이트에서 +127 바이트) 내에 있다는 것을 알고 있으면이를 사용하여보다 컴팩트 한 코드를 얻게됩니다. 그러나이를 사용하여 바깥 쪽 오프셋이있는 레이블로 점프하려고하면 이 범위에서는 대리자를 만들 때 예외가 throw됩니다. – Iridium

답변

8

ILGenerator.ILOffset은 IL 스트림의 현재 오프셋을 제공합니다 (원하는 경우). goric가 제안한대로 DefineLabelMarkLabel도 사용할 수 있습니다.

brtrue.sbrtrue의 유일한 차이는 brtrue.sbrtrue의 짧은 버전입니다. brtrue은 4 바이트 오프셋을 사용하고 brtrue.s은 1 바이트 오프셋을 사용합니다. brfalsebrfalse.s (및 br/br.s)에 대해서도 동일하게 적용됩니다.

이들은 IL 명령어의 유일한 짧은 버전이 아니며 정수로드를위한 ldc.i4.0 - ldc.i4.8과 같은 다른 짧은 명령어도 있습니다. 그것들은 주로 작은 바이너리 생성에 유용하지만, 그렇지 않으면 큰 차이가 없다고 생각합니다.

8
  1. 당신은 지점의 대상 위치를 결정하기 위해 DefineLabelMarkLabel 방법을 조합하여 사용할 수 있습니다. 그렇게하려면 equalnotequal과 같은 라벨을 선언하십시오. 그런 다음 라벨에 레이블이 있어야하는 일리노이의 지점을 표시 할 수 있습니다. 이 작업이 완료되면 분기 명령어의 대상을이 레이블로 설정할 수 있습니다.

    // 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. Br, BrtrueBrfalse 그들의 _S 대응 차이는 점프의 길이이다. _S은 짧은 형식을 나타냅니다. 대상 명령어는 다음 명령어의 부호있는 1 바이트 오프셋입니다. 표준 (비 짧음) 형식에서 대상은 4 바이트 오프셋으로 표시되는 입니다.

4

이 나에게 새로운,하지만 분명히 약간의 검색에서 사전에 어떤 시점에서

Label targetInstruction = g.DefineLabel(); 

를 호출하여 주소를 확인 (예를 들어, 첫 번째 Emit 전에?) 한 다음에 을 분기 할 선을 방출

g.MarkLabel(targetInstruction); 

직전. 그런 다음 Label이 귀하의 Br____ opcode에 대한 인수입니다.

Br, BrtrueBrfalse의 차이점은 Br이 항상 분기된다는 것이며, 다른 두 개는 스택에서 값을 팝하고 각각 참 또는 거짓이면 분기 만합니다. (그렇습니다. 큰 차이가 있습니다!) _S는 "짧은 양식"을 의미하지만 이것이 무엇을 의미하는지는 확실하지 않습니다.