IT

C # 또는 VB.NET에서 할 수없는 MSIL에서 무엇을 할 수 있습니까?

lottoking 2020. 6. 2. 21:15
반응형

C # 또는 VB.NET에서 할 수없는 MSIL에서 무엇을 할 수 있습니까? [닫은]


.NET 언어로 작성된 모든 코드는 MSIL로 컴파일되지만 MSIL 만 직접 사용할 수있는 특정 작업 / 작업이 있습니까?

MSIL에서 C #, VB.NET, F #, j # 또는 기타 .NET 언어보다 쉽게 ​​수행 할 수있는 작업을 살펴 ​​보겠습니다.

지금까지 우리는 이것을 가지고 있습니다 :

  1. 꼬리 재귀
  2. 일반 공분산
  3. 반환 유형 만 다른 과부하
  4. 액세스 수정 자 무시
  5. System.Object에서 상속 할 수없는 클래스가 있습니다.
  6. 필터링 된 예외 (vb.net에서 수행 가능)
  7. 현재 정적 클래스 유형의 가상 메소드를 호출합니다.
  8. 박스형 버전의 값 유형에 대한 핸들을 가져옵니다.
  9. 시도 / 결함을 수행하십시오.
  10. 금지 된 이름 사용.
  11. 값 유형에 대한 고유 한 매개 변수없는 생성자를 정의하십시오 .
  12. raise요소를 사용하여 이벤트를 정의하십시오 .
  13. CLR에서는 허용하지만 C #에서는 허용하지 않는 일부 변환이 있습니다.
  14. main()방법을 만들지 마십시오 .entrypoint.
  15. 기본 int및 기본 unsigned int유형으로 직접 작업하십시오.
  16. 과도 포인터로 재생
  17. MethodBodyItem의 emitbyte 지시어
  18. 비 System.Exception 유형을 던지고 잡습니다.
  19. 상속 열거 형 (확인되지 ​​않음)
  20. 바이트 배열을 (4 배 더 작은) 정수 배열로 취급 할 수 있습니다.
  21. 필드 / 방법 / 속성 / 이벤트는 모두 같은 이름 (확인되지 ​​않음)을 가질 수 있습니다.
  22. 자체 catch 블록에서 try 블록으로 다시 분기 할 수 있습니다.
  23. famandassem 액세스 지정자에 액세스 할 수 있습니다 ( protected internalfam 또는 assem 임 )
  24. <Module>전역 함수 또는 모듈 이니셜 라이저를 정의 하기 위해 클래스에 직접 액세스합니다 .

MSIL은 다음과 같은 이유로 인해 반환 유형 만 다른 과부하를 허용합니다.

call void [mscorlib]System.Console::Write(string)

또는

callvirt int32 ...

C # 및 VB를 포함한 대부분의 .Net 언어는 MSIL 코드의 꼬리 재귀 기능을 사용하지 않습니다.

테일 재귀는 기능적 언어에서 일반적인 최적화입니다. 메소드 B가 호출되면 메소드 A의 스택이 할당 해제 될 수 있도록 메소드 B의 값을 리턴하여 메소드 A가 종료 될 때 발생합니다.

MSIL 코드는 꼬리 재귀를 명시 적으로 지원하며 일부 알고리즘의 경우 중요한 최적화가 될 수 있습니다. 그러나 C # 및 VB는이를 수행하기위한 지시 사항을 생성하지 않으므로 수동으로 (또는 F # 또는 다른 언어를 사용하여) 수행해야합니다.

C #에서 tail-recursion을 수동으로 구현하는 방법의 예는 다음과 같습니다.

private static int RecursiveMethod(int myParameter)
{
    // Body of recursive method
    if (BaseCase(details))
        return result;
    // ...

    return RecursiveMethod(modifiedParameter);
}

// Is transformed into:

private static int RecursiveMethod(int myParameter)
{
    while (true)
    {
        // Body of recursive method
        if (BaseCase(details))
            return result;
        // ...

        myParameter = modifiedParameter;
    }
}

It is common practice to remove recursion by moving the local data from the hardware stack onto a heap-allocated stack data structure. In the tail-call recursion elimination as shown above, the stack is eliminated completely, which is a pretty good optimization. Also, the return value does not have to walk up a long call-chain, but it is returned directly.

But, anyway, the CIL provides this feature as part of the language, but with C# or VB it has to be implemented manually. (The jitter is also free to make this optimization on its own, but that is a whole other issue.)


In MSIL, you can have a class which cannot inherit from System.Object.

Sample code: compile it with ilasm.exe UPDATE: You must use "/NOAUTOINHERIT" to prevent assembler from auto inheriting.

// Metadata version: v2.0.50215
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}
.assembly sample
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
  .hash algorithm 0x00008004
  .ver 0:0:0:0
}
.module sample.exe
// MVID: {A224F460-A049-4A03-9E71-80A36DBBBCD3}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x02F20000


// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit Hello
{
  .method public hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       13 (0xd)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello World!"
    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000b:  nop
    IL_000c:  ret
  } // end of method Hello::Main
} // end of class Hello


It's possible to combine the protected and internal access modifiers. In C#, if you write protected internal a member is accessible from the assembly and from derived classes. Via MSIL you can get a member which is accessible from derived classes within the assembly only. (I think that could be pretty useful!)


Ooh, I didn't spot this at the time. (If you add the jon-skeet tag it's more likely, but I don't check it that often.)

It looks like you've got pretty good answers already. In addition:

  • You can't get a handle on the boxed version of a value type in C#. You can in C++/CLI
  • You can't do a try/fault in C# ("fault" is a like a "catch everything and rethrow at the end of the block" or "finally but only on failure")
  • There are lots of names which are forbidden by C# but legal IL
  • IL allows you to define your own parameterless constructors for value types.
  • You can't define events with a "raise" element in C#. (In VB you have to for custom events, but "default" events don't include one.)
  • Some conversions are allowed by the CLR but not by C#. If you go via object in C#, these will sometimes work. See a uint[]/int[] SO question for an example.

I'll add to this if I think of anything else...


The CLR supports generic co/contravariance already, but C# is not getting this feature until 4.0


In IL you can throw and catch any type at all, not just types derived from System.Exception.


IL has the distinction between call and callvirt for virtual method calls. By using the former you can force calling a virtual method of the current static class type instead of the virtual function in the dynamic class type.

C# has no way of doing this:

abstract class Foo {
    public void F() {
        Console.WriteLine(ToString()); // Always a virtual call!
    }

    public override string ToString() { System.Diagnostics.Debug.Assert(false); }
};

sealed class Bar : Foo {
    public override string ToString() { return "I'm called!"; }
}

VB, like IL, can issue nonvirtual calls by using the MyClass.Method() syntax. In the above, this would be MyClass.ToString().


In a try/catch, you can re-enter the try block from its own catch block. So, you can do this:

.try {
    // ...

  MidTry:
    // ...

    leave.s RestOfMethod
}
catch [mscorlib]System.Exception {
    leave.s MidTry  // branching back into try block!
}

RestOfMethod:
    // ...

AFAIK you can't do this in C# or VB


With IL and VB.NET you can add filters when catching exceptions, but C# v3 does not support this feature.

This VB.NET example is taken from http://blogs.msdn.com/clrteam/archive/2009/02/05/catch-rethrow-and-filters-why-you-should-care.aspx (note the When ShouldCatch(ex) = True in the Catch clause):

Try
   Foo()
Catch ex As CustomBaseException When ShouldCatch(ex)
   Console.WriteLine("Caught exception!")
End Try

As far as I know, there's no way to make module initializers (static constructors for an entire module) directly in C#:

http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx


Native types
You can work with the native int and native unsigned int types directly (in c# you can only work on an IntPtr which is not the same.

Transient Pointers
You can play with transient pointers, which are pointers to managed types but guaranteed not to move in memory since they are not in the managed heap. Not entirely sure how you could usefully use this without messing with unmanaged code but it's not exposed to the other languages directly only through things like stackalloc.

<Module>
you can mess about with the class if you so desire (you can do this by reflection without needing IL)

.emitbyte

15.4.1.1 The .emitbyte directive MethodBodyItem ::= … | .emitbyte Int32 This directive causes an unsigned 8-bit value to be emitted directly into the CIL stream of the method, at the point at which the directive appears. [Note: The .emitbyte directive is used for generating tests. It is not required in generating regular programs. end note]

.entrypoint
You have a bit more flexibility on this, you can apply it to methods not called Main for example.

have a read of the spec I'm sure you'll find a few more.


You can hack method override co/contra-variance, which C# doesn't allow (this is NOT the same as generic variance!). I've got more information on implementing this here, and parts 1 and 2


I think the one I kept wishing for (with entirely the wrong reasons) was inheritance in Enums. It doesn't seem like a hard thing to do in SMIL (since Enums are just classes) but it's not something the C# syntax wants you to do.


Here's some more:

  1. You can have extra instance methods in delegates.
  2. Delegates can implement interfaces.
  3. You can have static members in delegates and interfaces.

20) You can treat an array of bytes as a (4x smaller) array of ints.

I used this recently to do a fast XOR implementation, since the CLR xor function operates on ints and I needed to do XOR on a byte stream.

The resulting code measured to be ~10x faster than the equivalent done in C# (doing XOR on each byte).

===

I don't have enough stackoverflow street credz to edit the question and add this to the list as #20, if someone else could that would be swell ;-)


Something obfuscators use - you can have a field/method/property/event all have the same name.


Enum inheritance is not really possible:

You can inherit from an Enum class. But the result doesn't behave like an Enum in particular. It behaves not even like a value type, but like an ordinary class. The srange thing is: IsEnum:True, IsValueType:True, IsClass:False

But thats not particulary useful (unless you want to confuse a person or the runtime itself.)


You can also derive a class from System.Multicast delegate in IL, but you can't do this in C#:

// The following class definition is illegal:

public class YourCustomDelegate : MulticastDelegate { }


You can also define module-level (aka global) methods in IL, and C#, in contrast, only allows you to define methods as long as they are attached to at least one type.

참고URL : https://stackoverflow.com/questions/541936/what-can-you-do-in-msil-that-you-cannot-do-in-c-sharp-or-vb-net

반응형