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

بیان تجربیات و دیدگاه های یک برنامه نویس در مورد نرم افزار , طراحی و تولید برنامه با استفاده از تکنولوژی های مایکروسافت

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

بیان تجربیات و دیدگاه های یک برنامه نویس در مورد نرم افزار , طراحی و تولید برنامه با استفاده از تکنولوژی های مایکروسافت

 چنانچه میدانیم، یک Pointer تنها فقط آدرس قسمتی از حافظه است که در C و C++ از آن برای دستیابی به مقدار ذخیره شده در آن ناحیه از حافظه که آدرس آن توسط Pointer تامین میگردد ، استفاده میکنیم.
اما این آدرس همیشه اشاره گری به یک مقدار (Value) نیست و گاهی بر اساس نیاز ممکن است اشاره گری به یک متد یا Function باشد.
در حالت اول برای ساده تر بیان کردن مفهوم فوق میتوان گفت که اگر آدرس مقدار موجود در ناحیه ای از حافظه با نام P در متغیری از نوع Pointer به نام V ذخیره شده باشد، آنگاه V اشاره گری به P نامیده میشود.
چنانچه گفته شد این مقدار میتواند آدرس یک Function یا یک متد باشد که در این حالت Function Pointer یا اشاره گری به یک تابع نامیده میشود و کاربردهای بسیار متنوع و جالبی دارد. در این مطلب این موضوع ابتدا با یک مثال ساده بیان شده و در قسمتهای بعد تاکید اصلی من اشاره و بحث درباره این واقعیت است که مفهوم Function Pointer دقیقا در C# به عنوان منبع الهام مفهوم Delegates و حتی Events مورد استفاده قرار گرفته شده و در نهایت با استفاده از مفهوم Generics به تکامل رسیده و گسترش یافته است.
این موضوع در اغلب کتابهای مرجع مطرح میشود اما معمولا به سادگی از آن عبور میکنند.

مشخصا یک Function Pointer، حاوی آدرس تابعیست که Signature آن (منظور تعداد و نوع پارامترهای ورودی و همچنین نوع مقدار بازگشتی) دقیقا در زمان تعریف مشخص میگردد.
برای یادآوری و توضیح دقیق تر این مفهوم مثال ساده زیر در C می تواند کمک موثری باشد:

 

‪#‎include‬ “stdafx.h”
int addition(int a)
{
Return a+10;
}
Int pointerTest(int* a)
{
*a=100;
Return *a;
}
int main(int argc, _TCHAR* argv[])
{
int aVariable =10;
printf("%d\n", pointerTest(&aVariable));
int (*functionPointerInC)(int);
functionPointerInC = &addition;
printf("%d",(*functionPointerInC)(1) );
return 0;
}


بدیهیست که این برنامه خروجی زیر را ایجاد خواهد کرد:
100
11

در مثال فوق به جز تابع اصلی main دو تابع مختلف وجود دارند که اولی با نام addition یک پارامتر ورودی نوع int دریافت نموده و یک خروجی باز از نوع int عودت می دهد. دومین تابع با نام pointerTest که یک پارامتر ورودی از نوع اشاره گری به int دریافت نموده و مقدار بازگشتی آن نیز از نوع int می باشد.
در داخل main ابتدا متغیری به نام aVariable از نوع int تعریف و همزمان به مقدار مشخصی، مقدار دهی (Initialize) میگردد. در ادامه تابع pointerTest فراخوانی شده و آدرس متغیر aVariable به عنوان پارامتر ورودی وارد این تابع میشود. بر اساس جزییاتی که در تابع pointerTest مشاهده میکنیم، مقدار بازگشتی این تابع مقدار 100 خواهد بود.در سطر سوم تابع main، یک Function Pointer به نام (Identifier) نمادین functionPointerInC تعریف کرده ایم که بر اساس آنچه از Signature (تعداد و نوع پارامترها و نوع مقدار بازگشتی) این تابع مشخص است این اشاره گر میتواند شامل یا حاوی آدرس هر تابعی باشد که Signature آن با چیزی که در هنگام تعریف این اشاره گر مشخص شده، (یعنی یک پارامتر از نوع int و مقدار بازگشتی از نوع int) مطابقت داشته باشد.
اگر به تابع addition با دقت بیشتری نگاه کنیم، متوجه میشویم که این تابع دقیقا دارای چنین ویژگیهاییست. . همین امر سبب شده است که ما در ادامه آدرس این تابع را در متغیر functionPointerInC ذخیره کنیم.
اینک که functionPointerInC حاوی آدرس تابع addition میباشد، ما به سادگی قادر خواهیم بود که از طریق این متغیر اشاره گر ، تابع addition را فراخوانی نماییم. از آنجاییکه تابع addition نیازمند دریافت یک مقدار از نوع int به عنوان پارامتر ورودی میباشد ما آن را تامین نموده و توسط functionPointerInC آن را احضار نموده ایم .

...اما چنانچه قبلا نیز اشاره شد، طراحان C# مفهوم Function Pointer را هوشمندانه گسترش داده و آن را با ویژگیهای این زبان Object Oriented سازگار نموده اند. همین موضوع، یعنی استفاده از آن در یک زبان Object Oriented ، ما را ملزم میکند که در مورد Static Methods یا Instance Method تمهیدات جداگانه ای در نظر بگیریم. در C# نوع Delegate به طور غیر مستقیم از System.Delegate و یا در صورتیکه Delegate از نوع Multicast باشد از نوع System.MulticastDelegate منشعب (Derived) شده است.

نکته

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

برای آشنایی بهتر با Delegate و درک صحیحی از تفاوت بین Delegate و MulticastDelegate هیچ چیز بهتر از بیان یک مثال ساده نیست.

 

namespace DelegatesInCSharp
{
class Program
{
delegate int DelegateOfTheCalculator(int a, int b);
static void Main(string[] args)
{
DelegateHandler();
}
static void DelegateHandler()
{
StandardCalculator standardCalculator = new StandardCalculator();
DelegateOfTheCalculator delegateOfTheCalculator =
new DelegateOfTheCalculator(standardCalculator.Add);
Console.WriteLine("Sum of a and b is:{0}", delegateOfTheCalculator(80, 10));
Console.WriteLine("Please press <Enter> to exit...");
Console.ReadLine();
}
}
public class StandardCalculator
{
public int Add(int a, int b) { return a + b; }
}
}


در ابتدای کلاس Program یک Delegate با نام DelegateOfTheCalculator معرفی شده است که به روشنی Syntax دقیق متدی را که DelegateOfTheCalculator قرار است نمایندگی آن را به عهدا داشته باشد، مشخص نموده است.
در مثال ما متد Add که در واقع یک Instance Method متعلق به کلاس StandardCalculator میباشد دقیقا دارای شرایطی است که Delegate آن را تعیین نموده و انتظار آن را دارد.
از سوی دیگر یک متد static به نام DelegateHandler در کلاس Program وجود دارد که نمونه سازی کلاس StandardCalculator و در ادامه فراخوانی و یا به طور دقیق تر Invoke نمودن متد Add در همینجا انجام شده است.
در مهمترین قسمت متد DelegateHnadler چنانچه اشاره شد Invoke یا فراخوانی متد Add به طور غیر مستقیم توسط Delegate مورد نظر ما یعنی delegateOfTheCalculator انجام شده و چنانچه این متد انتظار دارد دو عدد از نوع int به ترتیب 80 و 10 به عنوان پارامترهای ورودی، وارد این متد شده و خروجی محاسبه شده یعنی عدد 90 توسط Writeline در خروجی ظاهر میشود.
همانطور که قبلا نیز اشاره کردیم مفهوم Delegate بسیار گسترده تر از Function Pointer بوده و مثال بالا نیز به روشنی نشان میدهد که استفاده از آن نیز بسیار راحت تر شده است. برای نمایش برخی از این تواناییها ما نیز مثال قبلی خود را با کمی تغییر گسترده تر نموده و در آن Multicast Delegate را بررسی میکنیم.

 

namespace DelegatesInCSharp
{
class Program
{
delegate int DelegateOfTheCalculator(int a, int b);
static void Main(string[] args)
{
DelegateHandler();
}
static void DelegateHandler()
{
StandardCalculator standardCalculator = new StandardCalculator();
DelegateOfTheCalculator delegateOfTheCalculator =
new DelegateOfTheCalculator(standardCalculator.Add);
delegateOfTheCalculator += standardCalculator.Sub;
delegateOfTheCalculator += standardCalculator.Mul;
delegateOfTheCalculator += standardCalculator.Div;
//delegateOfTheCalculator -= standardCalculator.Sub;
var addressOfAddMethod = typeof(StandardCalculator).GetMethod("Add").MethodHandle.
GetFunctionPointer();
var methodParams = new object[] { 80, 10 };
System.Reflection.MethodInfo miAdd = typeof(StandardCalculator).GetMethod("Add");
System.Reflection.MethodInfo miSub = typeof(StandardCalculator).GetMethod("Sub");
Console.WriteLine("Sum of a and b is:{0}", miAdd.Invoke(standardCalculator, methodParams));
Console.WriteLine("Result of subtracting b from a is:{0}", miSub.Invoke(standardCalculator, methodParams));
Console.WriteLine("Please press <Enter> to exit...");
Console.ReadLine();
}
}
public class StandardCalculator
{
public int Add(int a, int b) { return a + b; }
public int Sub(int a, int b) { return a > b ? a - b : 0; }
public int Mul(int a, int b) { return a * b; }
public int Div(int a, int b) { return ((a > b) && (b!=0)) ? a / b : 0; }
}
}

 

تفاوتها در اینجا بسیار آشکار میباشند. اولا لازم است به استفاده از اپراتور های Overload شده =+ و =- توجه کنیم. البته من برای آنکه فقط استفاده از اپراتور =- را یادآوری کنم، آن را ذکر کرده ولی آن را Comment کرده ام. در یک نگاه کلی تفاوت بارز در اینجاست که حالا delegateOfTheCalculator بر خلاف مثال قبلی که نمایندگی یک متد را به عهده داشت ، اینک به تعبیری اشاره گری به هر 4 متد کلاس StandardCalculator است . البته اگر بخواهیم به همان روش قبلی متد مورد نظر خودمان را فراخوانی کنیم، متوجه خواهیم شد که تنها امکان Invoke یا فراخوانی آخرین متدی که با استفاده از اپراتور =+ اضافه شده (یعنی متد Div) فراهم خواهد شد. پس برای غلبه بر این مشکل با کمی کمک از Reflection و بهره گیری از MethodInfo به ترتیبی که در مثال ملاحظه میکنیم قادر هستیم هر یک از چهار متد مورد نظر را در مواقع نیاز فراخوانی کنیم. همچنین لازم به ذکر است که من از متغیر Dtynamicی به نام addressOfAddMethod استفاده کرده ام و هدف من از ذکر آن در متن مثال، صرفا نشان دادن روشی برای بررسی برخی جزییاتی است که ممکن است بعضی از دوستان علاقمند بتوانند آدرس دقیق متدهای مورد نظر را در سیستم خود بررسی و مشاهده نمایند.
همچنین اولین پارامتر متد Invoke در این مثال لزوما یک Object از کلاس StandardCalculator میباشد و اگر متدهای این کلاس از نوع static در نظر گرفته شوند بودن یا نبودن مقدار مناسب در پارامتر اول هیچ تاثیری نداشته و حتی میتوان از مقدار null برای این پارامتر استفاده نمود.
با اشتیاق منتظر شنیدن نظرات سروران و دوستان محترم هستم. امیدوارم همه دوستان اشتباهات و کاستی های احتمالی را که مستقیما ناشی از بضاعت اندک من می باشد بخشیده و آن را گوشزد نمایند.

موافقین ۰ مخالفین ۰ ۹۳/۰۷/۳۰
مهران حسین نیا

نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی