کلمات کلیدی ref و out در #C

سه شنبه 6 مرداد 1394

در این مقاله انواع داده مقداری و ارجاعی را بررسی خواهیم کرد. همچنین به طور خاص بر روی کلمات کلیدی ref و out تمرکز می نماییم.

کلمات کلیدی ref و out در #C

قبل از اینکه به کلمات کلیدی ref و out بپردازیم، لازم است که بدانیم چه انواع داده مقداری و ارجاعی وجود دارند. بنابراین اجازه دهید با انواع ارجاعی شروع کنیم.

نوع داده ارجاعی، یک نوع اشاره گر است که آدرسی از حافظه را نگه می دارد که به یک محلی از حافظه Heap اشاره می کند. نکته کلیدی اینجاست که نوع داده ارجاعی از هر دوی Stack و Heap استفاده می کند. به این معنی که آدرس را در Heap ذخیره می کند و مقدار در Stack ذخیره می شود، بنابراین بدون Stack، اصلا Heap وجود ندارد.

مثال: تمام کلاس های تعریف شده توسط کاربر

انواع مقداری، مقدار متغیر را در خودشان ذخیره می کنند و از حافظه stack استفاده می کنند.

مثال: تمام انواع داده ای از پیش تعریف شده (مثل int، float،doble و...)

int x=4;  
int x1=x;  
then again if x=100; 

اگر خروجی x1 را چاپ کنیم، 4 را به عنوان خروجی نمایش می دهد و با وجود تغییر x، متغیر دوم تغییر نمی کند. زیرا یک مقدار را مستقیما در خود نگه می دارد. اما اگر یک نوع ارجاعی تغییر کند، تمام متغیرهایی که به آن اشاره می کنند هم تغییر می کنند.

به بحث اصلی خودمان یعنی کلمات کلیدی ref و out برمی گردیم.

کلمه کلیدی ref:

کلمه کلیدی ref می تواند به همراه هر نوع داده ای استفاده شود. هدف اصلی کلمه کلیدی ref، ایجاد یک نوع داده ارجاعی از هر نوع داده ای است. به عبارت دیگر زمانی که ما در فراخوانی متدی از این کلمه کلیدی استفاده می کنیم، کامپایلر #C متغیر جدیدی نمی سازد و فقط یک کپی از آن نوع داده می سازد که به متغیری که ما تعریف کرده ایم، اشاره می کند و تغییرات روی این متغیر، بر متغیر جدید هم اعمال می شود.

مثال زیر را در نظر بگیرید:

    int x=10;  
    show(ref x)  
    //Method declaration  

Public static show (ref int number): اینجا متغیر جدیدی ساخته نمی شود بلکه یک کپی از متغیر X ساخته می شود و هر تغییری درون تابع ایجاد شود، روی متغیر اصلی اعمال می شود. نحوه استفاده از ref در انواع داده اولیه به این صورت است.

مثال:

    class RefDemo  
    {  
       public static void Show(int number)  
       {  
          number= 23;  
       }  
       static void Main()  
       {  
          int x=10;  
          Show(x);  
          Console.WriteLine(x);   
       }  
    }  

خروجی مثال فوق، 10 می باشد. زیرا تمام انواع مقداری به طور مستقیم، مقدار را نگه می دارند و به همین دلیل خروجی برای متغیر number، عدد 23 و برای x عدد 10 است.

اما اگر این متد را با ref فراخوانی کنیم، تغییر خواهد کرد. مانند کد زیر:

    class RefDemo  
    {  
       public static void Show(ref int number)  
       {  
          number= 23;  
       }  
       static void Main()  
       {  
          int x=10;  
          Show(ref x);  
          Console.WriteLine(x);   
       }  
    }  

خروجی این کد 23 خواهد بود، زیرا کامپایلر #C متغیر جدیدی نمی سازد و از نوع متغیر x یک کپی می سازد. بنابراین هر تغییری روی x هم اعمال می شود.

یک کلاس و تمام متغیرها با نوع داده ارجاعی همین طور عمل می کنند. همان طور که می دانیم، زمانی که یک شی از کلاس بسازیم، بدون استفاده از ref هم، ، همه متغیرهای ارجاعی به همان شی اشاره می کنند. مثال زیر را در نظر بگیرید.

class RefDemo  
{  
   public static void Show(GradeData data) //GradeData is class  
   {  
      data.name= "Alice";  
   }  
   static void Main()  
   {  
      GradeData obj=new GradeData();  
      GradeData obj1;  
      obj1.name="Bob";  
      obj1=obj;  
      Show(obj1)  
      Console.WriteLine(obj1.name);   
   }  
} 

اینجا، خروجی ما "Alice" خواهد بود.

به مثال دوم توجه کنید.

class RefDemo  
{  
   public static void Show(GradeData data) //GradeData is class  
   {  
      GradeData data=new GradeData();  
      data.name= "Alice";  
   }  
   static void Main()  
   {  
      GradeData obj=new GradeData();  
      GradeData obj1;  
      obj1.name="Bob";  
      obj1=obj;  
      Show(obj1)  
      Console.WriteLine(obj1.name);   
   }  
} 

خروجی کد بالا "Bob" خواهد بود نه Alice. چون با اینکه ما obj1 را به عنوان پارامتر ارسال کردیم اما نمی توانیم "Alice" را به عنوان خروجی بگیریم. چرا که data در این مثال یک شی جدید است، اما در مثال قبل فقط یک متغیر بود.

بنابراین برای اینکه یک شی جدید بسازیم و به همان متغیر هم اشاره کند، از کلمه کلیدی ref استفاده می کنیم.

مثال:

class RefDemo  
{  
   public static void Show(ref GradeData data) //GradeData is class  
   {  
      GradeData data=new GradeData();  
      data.name= "Alice";  
   }  
   static void Main()  
   {  
      GradeData obj=new GradeData();  
      GradeData obj1;  
      obj1.name="Bob";  
      obj1=obj;  
      Show(ref obj1)  
      Console.WriteLine(obj1.name);   
   }  
}

خروجی این کد "Alice" است. زیرا به دلیل استفاده از کلمه کلیدی، کامپایلر #C با شیء data مانند یک کپی از obj1 برخورد می کند، بنابراین هر تغییری در data روی obj1/obj هم تاثیر می گذارد.

نکته: قبل از استفاده کلمه کلیدی ref در فراخوانی متد، باید مقداردهی اولیه شود چه از نوع مقداری باشد و چه از نوع ارجاعی.

کلمه کلیدی out:

out نیز برای همین هدف استفاده می شود و می توانیم برای هر دو نوع داده مقداری و ارجاعی از آن استفاده کنیم اما تفاوت عمده این دو کلمه کلیدی را در ادامه، بحث خواهیم کرد.

قبل از فراخوانی یک متد با ref، باید یک مقدار به متغیری که در متد استفاده می شود، تخصیص داده شود. درحالی که برای استفاده از out نیازی به مقداردهی اولیه نیست. به عبارت دیگر، برای کامپایلر #C مهم نیست که متغیر را مقداردهی کنیم یا خیر، اما زمانی که Controller به متد خاصی می رود، باید قبل از اینکه از متد خارج شود متغیر را مقداردهی کنیم.

تفاوت دیگر این است که زمانی که out استفاده می شود، می تواند همزمان چند مقدار را به عنوان داده برگشتی توسط متد برگرداند.

مثال:

class OutDemo  
{  
   public static void GetData(int c, out int a, out int b)  
   {  
      a = c + 5;  
      b = c + a;  
   }  
   static void Main()  
   {  
      int a,b;  
      GetData(5, out a, out b);  
      Console.WriteLine(a + "\n" + b);  
      Console.ReadLine();  
   }  
}

این کد a= 10 و b= 15 را نمایش می دهد.

نکته آخر:

به خاطر داشته باشید که ترکیب استفاده هر دوی این ها توسط کامپایلر نادیده گرفته می شود و کامپایلر ساختار استاندارد زبان را در نظر می گیرد. پس اگر کدی مانند کد زیر نوشته باشید:


    public static void Getdata(ref int a)  
    {  
    }  
    public static void Getdata(out int a)  
    {  
    }  

و آن را یک متد  overload شده در نظر بگیرید، در اشتباه هستید. در این حالت، کامپایلر ref و out را پارامتر در نظر نمی گیرد و با یک خطای در حال اجرا (runtime) مواجه می شوید.

امیدوارم این مقاله تصویر واضحی از ref و out به شما ارائه داده باشد.

آموزش سی شارپ

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • C#.net
  • 3k بازدید
  • 2 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید