Msba
28-03-2013, 01:11
امروز به صورت اتفاقی متوجه شدم که برنامه نویسان دو مطلب را فراموش می کنند و این فراموشی سبب می شود که خطاهای جبران ناپذیری ایجاد کنند. در واقع یک حفره امنیتی در نرم افزار هاشون ایجاد کنند. این اشتباه ممکن است باعث نفوذ هکر ها در سیستم استفاده کنندگان شود، خصوصا نرم افزار هایی که در محیط های شبکه ای اعم از اینترنت و LAN استفاده می شوند.
البته این فقط یک احتمال و یک هشدار است و ممکن است هیچگاه اتفاق نیوفتد.
توضیحات کمی پایه ای تر خواهد بود تا سطح های مختلف بتوانند استفاده کنند.
اصل مطلب:
دو نوع از منابع حافظه در .NET وجود دارد. منابعی که به مانند مقادیر بررسی شده و منابعی که عددی بررسی نخواهند شد.
منابعی که به صورت عددی بررسی خواهند شد شامل :
متغیر های عددی :
برای مشاهده محتوا ، لطفا وارد شوید یا ثبت نام کنید
به همراه unsigned آن ها نظیر uint .
متغیر های Enum .
و مهم تر از همه struct ها.
هر struct ی که توسط برنامه نویس تعریف شده باشد و یا اینکه در .Net وجود داشته باشد به عنوان یک عنصر مقداری شناخته می شود.
به این دسته همان طور که مشخص است ValueType گفته می شود.
بار ها از این دسته استفاده شده است و عموما در این دسته برای برنامه نویسان مشکلی وجود نخواهد داشت.
-----------------------
دسته ی دوم شامل:
کلاس ها، چه برنامه نویس ایجاد کند چه از .Net باشد.
delegate ها
interface ها
عناصر object
string ها
dynamic ها
تمامی آرایه ها حتی اگر نوع آن ها int باشد.(چرا؟ چون که تمامی آرایه ها از System.Array ارث بر هستند و این کلاس است! در ضمن خود System.Array از object ارث برده است که آن هم refrenceType هست.)
-----------------------
یک توضیح کوتاه لازم است که بیان شود چرا این عناصر با هم متفاوت هستند:
نگاهی به حافظه می اندازیم، سه بخش کلیدی وجود دارد:
HEAP
Stack
Reserved
بخش سوم عموما مربوط به سیستم های عامل بوده و توسط آن محافظت می شود و قابل دسترسی نیست اما در دو نوع دیگر برنامه نویس تصمیم گیر است.
متغیر های محلی، آرگومان های متد ها؛ و در دید نوع گرایی عموما ValueType ها در stack قرار می گیرند.( کدام stack ؟ هر Thread برای خود یک stack از بخش stack پروسس اصلی بر می دارد. منظور این استک می باشد.)
تمام content های refrence-Type ها در Heap قرار می گیرد.( توضیح اضافه: دو نوع heap وجود دارد: managed heap و unmanaged heap، اینجا منظور heap است که manage است و توسط garbage collector قابل دسترس هست.)
پس با این تعاریف یک کلاس می تواند هم در استک فضا گرفته باشد و هم در هیپ.
حال بحث مربوط می شود به دو اصل: class و struct
شما زمانی که از struct استفاده می کنید یک فضا از stack میگیرید اما زمانی که از class استفاده می کنید Heap نقش خود را بازی می کند. ( در هر صورت حافظه manage شده است که در C# و یا VB.net کسی به این موارد نیاندیشد و از معایب این عمل سست شدن برنامه ی برنامه نویس خواهد بود.)
مهم ترین بحث در این دو دسته که باعث شد این مطالب گفته شود by ref و by value است.
اگر یک value-Type به یک متد ارسال شود در حالت عادی مقدار آن منتقل می گردد. و هر تغییری که در آن انجام شود هیچ تغییری در اصل متغیر نخواهد داد. این مطلب را بار ها دیده ایم.
اگر بخواهیم که متد یا تابع ما مقدار را تغییر دهد یک ref مشکل را حل می کند، اما در نوع دوم یعنی RefrenceType ها اینگونه نیست.
یعنی اگر یک کلاس به یک متد ارسال شود و content های آن اعم از Field، Property، Method و... تغییر کند خود شی تغییر کرده است و در بازگشت از متد این تغییرات قابل استفاده هستند. این رفتار بار ها باعث خطا برای برنامه نویس ها شده است.
پس by ref یا همان ref در این نوع چه استفاده ای دارد؟
Ref کردن یک Refrence-Type به معنای اجازه دادن تعریف مجدد آن توسط متد است. یعنی یک instance جدید.یک null و.... (مثال کد در ادامه)
فرض خانه ی 100 رم توسط شی من اشغال شده باشد آنگاه:
بدون ref : شی منتقل شده و تنها اجزای آن تغییر می کند. یک instance جدید هیچ تغییری در شی ایجاد نمی کند و یا اینکه شی را نمی شود null کرد.
با ref : با یک instance جدید، نه تنها مقادیر را تغییر می دهم بلکه هم اکنون شی من در خانه ی (مثال) 150 قرار دارد.
حال مشکل امنیت مشخص گردید. اگر شما یک DLL را نوشته اید و فراخوانی می کنید که به آن یک شی RefrenceType می فرستید اگر لازم است که شی شما مجدد تعریف گردد که مشکلی وجود ندارد اما اگر فقط مقادیر می خواهند تغییر کنند پس by ref بی معنی است. چرا چون یک هکر dll خود را جایگزین dll شما می کند و شی شما را کامل در دست می گیرد و با تعییر شی شما درنقطه مورد نظر و الی آخر....
پس می بایست در ارجاع با مقدار یا منبع (آدرس در unmanaged) دقت شود.
به این کد دقت کنید:
برای مشاهده محتوا ، لطفا وارد شوید یا ثبت نام کنید
اول مثال را امتحان کنید.
در یک بخش از مثال تغییر string یک struct مد نظر است.
*تعاریف خط بعد از میکروسافت است*
همان طور که مشخص است string یک RefrenceType هست و Struct یک value . پس رشته انتقالی چه می شود؟ رشته زمانی که به صورت رشته منتقل شود تغییرات را باید بپذیرد؛
اما زمانی که در قالب یک Struct منتقل می شود تحت رفتار آن خواهد بود.(مبحث مربوط به تئوری های شی گرایی) و در نتیجه تغییرات آن منتقل نمی گردند.
تناقض در خط آخر:
حال تناقض اینجا خواهد بود:
برای مشاهده محتوا ، لطفا وارد شوید یا ثبت نام کنید
و این اجراگر:
برای مشاهده محتوا ، لطفا وارد شوید یا ثبت نام کنید
همان طور که پس از اجرا می بینید رشته تغییر نکرد. پس رشته می شود استثنایی برای refrence-Type ها .
یعنی چه در struct باشد و چه به صورت تک نیازمند ref برای تغیرات هست. اما در یک کلاس نیازمند ref نیست.
نکته:
اگر ارجاع کلاس باشد ref برای String ها نیاز نیست. اگر یک string را از یک کلاس مستقیم ارجاع کنیم نیازمند ref خواهد بود.
موفق باشید.
البته این فقط یک احتمال و یک هشدار است و ممکن است هیچگاه اتفاق نیوفتد.
توضیحات کمی پایه ای تر خواهد بود تا سطح های مختلف بتوانند استفاده کنند.
اصل مطلب:
دو نوع از منابع حافظه در .NET وجود دارد. منابعی که به مانند مقادیر بررسی شده و منابعی که عددی بررسی نخواهند شد.
منابعی که به صورت عددی بررسی خواهند شد شامل :
متغیر های عددی :
برای مشاهده محتوا ، لطفا وارد شوید یا ثبت نام کنید
به همراه unsigned آن ها نظیر uint .
متغیر های Enum .
و مهم تر از همه struct ها.
هر struct ی که توسط برنامه نویس تعریف شده باشد و یا اینکه در .Net وجود داشته باشد به عنوان یک عنصر مقداری شناخته می شود.
به این دسته همان طور که مشخص است ValueType گفته می شود.
بار ها از این دسته استفاده شده است و عموما در این دسته برای برنامه نویسان مشکلی وجود نخواهد داشت.
-----------------------
دسته ی دوم شامل:
کلاس ها، چه برنامه نویس ایجاد کند چه از .Net باشد.
delegate ها
interface ها
عناصر object
string ها
dynamic ها
تمامی آرایه ها حتی اگر نوع آن ها int باشد.(چرا؟ چون که تمامی آرایه ها از System.Array ارث بر هستند و این کلاس است! در ضمن خود System.Array از object ارث برده است که آن هم refrenceType هست.)
-----------------------
یک توضیح کوتاه لازم است که بیان شود چرا این عناصر با هم متفاوت هستند:
نگاهی به حافظه می اندازیم، سه بخش کلیدی وجود دارد:
HEAP
Stack
Reserved
بخش سوم عموما مربوط به سیستم های عامل بوده و توسط آن محافظت می شود و قابل دسترسی نیست اما در دو نوع دیگر برنامه نویس تصمیم گیر است.
متغیر های محلی، آرگومان های متد ها؛ و در دید نوع گرایی عموما ValueType ها در stack قرار می گیرند.( کدام stack ؟ هر Thread برای خود یک stack از بخش stack پروسس اصلی بر می دارد. منظور این استک می باشد.)
تمام content های refrence-Type ها در Heap قرار می گیرد.( توضیح اضافه: دو نوع heap وجود دارد: managed heap و unmanaged heap، اینجا منظور heap است که manage است و توسط garbage collector قابل دسترس هست.)
پس با این تعاریف یک کلاس می تواند هم در استک فضا گرفته باشد و هم در هیپ.
حال بحث مربوط می شود به دو اصل: class و struct
شما زمانی که از struct استفاده می کنید یک فضا از stack میگیرید اما زمانی که از class استفاده می کنید Heap نقش خود را بازی می کند. ( در هر صورت حافظه manage شده است که در C# و یا VB.net کسی به این موارد نیاندیشد و از معایب این عمل سست شدن برنامه ی برنامه نویس خواهد بود.)
مهم ترین بحث در این دو دسته که باعث شد این مطالب گفته شود by ref و by value است.
اگر یک value-Type به یک متد ارسال شود در حالت عادی مقدار آن منتقل می گردد. و هر تغییری که در آن انجام شود هیچ تغییری در اصل متغیر نخواهد داد. این مطلب را بار ها دیده ایم.
اگر بخواهیم که متد یا تابع ما مقدار را تغییر دهد یک ref مشکل را حل می کند، اما در نوع دوم یعنی RefrenceType ها اینگونه نیست.
یعنی اگر یک کلاس به یک متد ارسال شود و content های آن اعم از Field، Property، Method و... تغییر کند خود شی تغییر کرده است و در بازگشت از متد این تغییرات قابل استفاده هستند. این رفتار بار ها باعث خطا برای برنامه نویس ها شده است.
پس by ref یا همان ref در این نوع چه استفاده ای دارد؟
Ref کردن یک Refrence-Type به معنای اجازه دادن تعریف مجدد آن توسط متد است. یعنی یک instance جدید.یک null و.... (مثال کد در ادامه)
فرض خانه ی 100 رم توسط شی من اشغال شده باشد آنگاه:
بدون ref : شی منتقل شده و تنها اجزای آن تغییر می کند. یک instance جدید هیچ تغییری در شی ایجاد نمی کند و یا اینکه شی را نمی شود null کرد.
با ref : با یک instance جدید، نه تنها مقادیر را تغییر می دهم بلکه هم اکنون شی من در خانه ی (مثال) 150 قرار دارد.
حال مشکل امنیت مشخص گردید. اگر شما یک DLL را نوشته اید و فراخوانی می کنید که به آن یک شی RefrenceType می فرستید اگر لازم است که شی شما مجدد تعریف گردد که مشکلی وجود ندارد اما اگر فقط مقادیر می خواهند تغییر کنند پس by ref بی معنی است. چرا چون یک هکر dll خود را جایگزین dll شما می کند و شی شما را کامل در دست می گیرد و با تعییر شی شما درنقطه مورد نظر و الی آخر....
پس می بایست در ارجاع با مقدار یا منبع (آدرس در unmanaged) دقت شود.
به این کد دقت کنید:
برای مشاهده محتوا ، لطفا وارد شوید یا ثبت نام کنید
اول مثال را امتحان کنید.
در یک بخش از مثال تغییر string یک struct مد نظر است.
*تعاریف خط بعد از میکروسافت است*
همان طور که مشخص است string یک RefrenceType هست و Struct یک value . پس رشته انتقالی چه می شود؟ رشته زمانی که به صورت رشته منتقل شود تغییرات را باید بپذیرد؛
اما زمانی که در قالب یک Struct منتقل می شود تحت رفتار آن خواهد بود.(مبحث مربوط به تئوری های شی گرایی) و در نتیجه تغییرات آن منتقل نمی گردند.
تناقض در خط آخر:
حال تناقض اینجا خواهد بود:
برای مشاهده محتوا ، لطفا وارد شوید یا ثبت نام کنید
و این اجراگر:
برای مشاهده محتوا ، لطفا وارد شوید یا ثبت نام کنید
همان طور که پس از اجرا می بینید رشته تغییر نکرد. پس رشته می شود استثنایی برای refrence-Type ها .
یعنی چه در struct باشد و چه به صورت تک نیازمند ref برای تغیرات هست. اما در یک کلاس نیازمند ref نیست.
نکته:
اگر ارجاع کلاس باشد ref برای String ها نیاز نیست. اگر یک string را از یک کلاس مستقیم ارجاع کنیم نیازمند ref خواهد بود.
موفق باشید.