نوشتن کد IL در ویژوال استودیو

MSIL مخفف Microsoft Intermedia Language یک زیان اسمبلی NET. است که تحت نام Common Intermediate Language (CIL) استانداردسازی شده است. تمام کامپایلرهای NET. به کد منبعی برای این زبان تبدیل می‌شوند. این مقاله نحوه نوشتن کد IL در ویژوال استودیو را بررسی می‌کند.

نوشتن کد IL در ویژوال استودیو

اگرچه حالتی داریم که مجبوریم در آن کد intermedia language (IL) را مستقیما بنویسیم، خوب است که نحوه کار آن را بشناسیم و بدانیم که چگونه توسط ویژوال استودیو پشتیبانی می‌شود.

پشتیبانی IL توسط ویژوال استودیو

حالاتی در هنگام نوشتن بعضی قسمت‌های کد در IL وجود دارد، که عملکرد بهتری را ارائه خواهد داد یا برنامه‌ای که منابع کمتری مصرف می‌کند را می‌سازد. نوشتن مستقیم IL برای سولوشن‌های C# یا VB.NET در ویژوال استودیوی اولیه که پیکربندی خاصی نشده است پشتیبانی نمی‌شود. برای داشتن پشتیبانی IL در این سولوشن‌ها می‌توانیم از افزونه‌های بسیار خوب برای ویژوال استودیو استفاده کنیم (IL Support توسط ins0mniaque). اکثر نسخه‌های جدیدتر ویژوال استودیو از جمله ویژوال استودیو 2017 پشتیبانی می‌شوند.

پس از نصب افزونه، قالب‌های جدید برنامه برایمان ایجاد می‌شود و می‌توانیم فایل‌های IL را به سولوشن موجود اضافه کنیم.

قالب‌های برنامه

قالب‌های جدید برنامه نسخه‌هایی از موارد موجود هستند با تفاوت‌هایی که IL را پشتیبانی می‌کنند.

برای بررسی کلی آنچه که وجود دارد، بیایید برنامه کنسول را ایجاد کرده و ببینیم چه چیزی در آن موجود است.

برنامه IL پیش‌فرض

برنامه کنسول پیش‌فرض با IL شامل دو فایل مهم است. یکی از آن‌ها Program.cs با متد Main() است که نقطه ورود برنامه کنسول است. کلاس Program در اینجا نشان داده شده است.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;

namespace ILConsoleApp
{
     class Program

     {

         static void Main(string[] args)

         {

         }


         [MethodImpl(MethodImplOptions.ForwardRef)]

         public static extern int Square(int number);

     }
}

حالا سراغ فایل دیگر می‌رویم، Program.il جایی که یک متد در IL نوشته شده است.

.class public ILConsoleApp.Program
{
  .method public static int32 Square(int32 number) cil managed
  {
     .maxstack 2
     ldarg.0
     dup
     mul
     ret
  }
}

این متد مجذوری از اعداد به دست آمده را محاسبه می‌کند. کسانی که حداقل برخی از تجربه‌های جزئی زبان اسمبلی را دارند احتمالا کد را بدون توضیح درک می‌کنند. برای آنهایی که چیزی در این باره نمی‌دانند هم در اینجا آن را تفسیر کرده‌ایم.

.class public ILConsoleApp.Program
{
  .method public static int32 Square(int32 number) cil managed
  {
     .maxstack 2   // maximum stack depth used by the function code
     ldarg.0       // loads the first argument to the evaluation stack
     dup           // duplicates the value at the top of the stack
     mul           // pops two values from the stack, multiplies and pushes product to stack
     ret           // returns top value from stack (product of mul)
  }
}

همانطور که می‌بینید چیز زیاد پیچیده‌ای نیست. فقط باید نگرشی در مورد زبان اسمبلی به دست آوریم.

اعلان متد Square()

حالا بیایید اعلان متد Square() در فایل Program.cs را ببینیم.

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern int Square(int number);

 ویژوال استودیو چیزی در مورد تعریف متد Square() در IL نمی‌داند ما باید به آن بگوییم که متدی همانند این وجود دارد، اما قبلا تعریف شده است. کلمه کلیدی extern می‌گوید که متدی به نام Square() در این کلاس وجود دارد اما در جای دیگری تعریف شده است. اتربیوت MethodImpl با ForwardRef می‌گوید این متد قبلا تعریف شده است، بنابراین نیازی نیست تا در این مرحله به دنبال آن بگردد.

امتحان کد

اکنون اجازه دهید متد Main() را تغییر دهیم و Square() را فراخوانی کنیم تا ببینیم آیا کار می‌کند.

using System;
using System.Runtime.CompilerServices;

namespace ILConsoleApp
{
class Program
     {
         static void Main(string[] args)
         {
             Console.WriteLine("4 * 4 = " + Square(4));
             Console.ReadKey();
         }
         [MethodImpl(MethodImplOptions.ForwardRef)]
         public static extern int Square(int number);

     }
}

همان‌طور که می‌بینید IntelliSense مانند هر متد دیگری که در کدمان استفاده می‌کنیم، به خوبی با متد Square() کار می‌کند. این خروجی برنامه ماست.

16 = 4 * 4

پشت پرده کامپایلر

آخرین کاری که باید انجام دهیم این است که به پشت پرده کامپایلر نگاهی بیندازیم و ببینیم چه چیزی توسط آن ساخته می‌شود. کد ما در ویرایشگر دیده می‌شود و آن چیزی که برای باینری تولید می‌شود می‌تواند به دلیل بهینه‌سازی کامپایلر و قواعد سینتکسی آن مثل انواع anonymouها یا فوت و فن‌هایی مثل extension methodها متفاوت باشد.

برای دیدن آنچه که کامپایلر می‌سازد بیایید کدی را با پیکربندی Release، حذف PDB-file و باز کردن آن در dotPeek بسازیم.

// Decompiled with JetBrains decompiler
// Type: ILConsoleApp.Program
// Assembly: ILConsoleApp,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null
// MVID: FC126C9C-C5B2-4C61-8905-784386252C96
// Assembly location: C:\projects\ILConsoleApp\bin\Release\ILConsoleApp.exe
 
using System;
 
namespace ILConsoleApp
{
     internal class Program
     {
         private static void Main(string[] args)
         {
             Console.WriteLine("4 * 4 =" + (object)Program.Square(4));
             Console.ReadKey();
         }
         public static int Square(int number)
         {
             int num = number;
             return num * num;
         }
     }
}

ما می‌توانیم به وضوح ببینیم که متد Square() مانند هر متد دیگری از کلاس Program() کامپایل می‌شود و اینکه در IL نوشته شده است زیاد مهم نیست. همچنین ما MethodImplAttribute دیگری و سایر مشخصاتی که کامپایلر را هدایت می‌کند را نمی‌بینیم.

نتیجه‌گیری

حالاتی در هنگام نوشتن بعضی قسمت‌های کد در IL وجود دارد، که مزایایی مثل عملکرد بهتر یا مصرف منابع کمتر برای ما دارد. همچنین می‌توانیم کمترین پیاده‌سازی از توابع محبوب که به طور پیش‌فرض خوب اجرا نمی‌شوند را بنویسیم. IL مشابه زبان اسمبلی است. هیچ پشتیبانی برای آن در ویژوال استودیویی که پیکربندی خاصی روی آن نشده است وجود ندارد، اما افزونه‌هایی در دسترس هستند که IL را برای ویژوال استودیو به همراه دارند. اگرچه مجبور بودیم از برخی کامپایلر‌ها در کد استفاده کنیم، اما کار چندان سختی نبود.