اشتراکها
آزمون واحد کلاس نگاشت تهیه شده
در مورد آشنایی با آزمونهای واحد لطفا به برچسب مربوطه در سمت راست سایت مراجعه بفرمائید. همچنین در مورد اینکه چرا به این نوع API کلمه Fluent اطلاق میشود، میتوان به تعریف آن جهت مطالعه بیشتر مراجعه نمود.
در این قسمت قصد داریم برای بررسی وضعیت کلاس نگاشت تهیه شده یک آزمون واحد تهیه کنیم. برای این منظور ارجاعی را به اسمبلی nunit.framework.dll به پروژه UnitTests که در ابتدای کار به solution جاری در VS.Net افزوده بودیم، اضافه نمائید (همچنین ارجاعهایی به اسمبلیهای پروژه NHSample1 ، FluentNHibernate ، System.Data.SQLite ، NHibernate.ByteCode.Castle و Nhibernate نیز نیاز هستند). تمام اسمبلیهای این فریم ورکها از پروژه FluentNHibernate قابل استخراج هستند.
سپس سه کلاس زیر را به پروژه آزمون واحد اضافه خواهیم کرد.
کلاس TestModel : (جهت مشخص سازی محل دریافت اطلاعات نگاشت)
using FluentNHibernate;
using NHSample1.Domain;
namespace UnitTests
{
public class TestModel : PersistenceModel
{
public TestModel()
{
AddMappingsFromAssembly(typeof(CustomerMapping).Assembly);
}
}
}
کلاس FixtureBase : (جهت ایجاد سشن NHibernate در ابتدای آزمون واحد و سپس پاکسازی اشیاء در پایان کار)
using NUnit.Framework;
using NHibernate;
using FluentNHibernate;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
namespace UnitTests
{
public class FixtureBase
{
protected SessionSource SessionSource { get; set; }
protected ISession Session { get; private set; }
[SetUp]
public void SetupContext()
{
var cfg = Fluently.Configure().Database(SQLiteConfiguration.Standard.InMemory);
SessionSource = new SessionSource(
cfg.BuildConfiguration().Properties,
new TestModel());
Session = SessionSource.CreateSession();
SessionSource.BuildSchema(Session);
}
[TearDown]
public void TearDownContext()
{
Session.Close();
Session.Dispose();
}
}
}
و کلاس CustomerMapping_Fixture.cs : (جهت بررسی صحت نگاشت تهیه شده با کمک دو کلاس قبل)
using NUnit.Framework;
using FluentNHibernate.Testing;
using NHSample1.Domain;
namespace UnitTests
{
[TestFixture]
public class CustomerMapping_Fixture : FixtureBase
{
[Test]
public void can_correctly_map_customer()
{
new PersistenceSpecification<Customer>(Session)
.CheckProperty(c => c.Id, 1001)
.CheckProperty(c => c.FirstName, "Vahid")
.CheckProperty(c => c.LastName, "Nasiri")
.CheckProperty(c => c.AddressLine1, "Addr1")
.CheckProperty(c => c.AddressLine2, "Addr2")
.CheckProperty(c => c.PostalCode, "1234")
.CheckProperty(c => c.City, "Tehran")
.CheckProperty(c => c.CountryCode, "IR")
.VerifyTheMappings();
}
}
}
توضیحات:
اکنون به عنوان یک برنامه نویس متعهد نیاز است تا کار صورت گرفته در قسمت قبل را آزمایش کنیم.
کار بررسی صحت نگاشت تعریف شده در قسمت قبل توسط کلاس استاندارد PersistenceSpecification فریم ورک FluentNHibernate انجام خواهد شد (در کلاس CustomerMapping_Fixture). این کلاس برای انجام عملیات آزمون واحد نیاز به کلاس پایه دیگری به نام FixtureBase دارد که در آن کار ایجاد سشن NHibernate (در قسمت استاندارد SetUp آزمون واحد) و سپس آزاد سازی آن را در هنگام خاتمه کار ، انجام میدهد (در قسمت TearDown آزمون واحد).
این ویژگیها که در مباحث آزمون واحد نیز به آنها اشاره شده است، سبب اجرای متدهایی پیش از اجرا و بررسی هر آزمون واحد و سپس آزاد سازی خودکار منابع خواهند شد.
برای ایجاد یک سشن NHibernate نیاز است تا نوع دیتابیس و همچنین رشته اتصالی به آن (کانکشن استرینگ) مشخص شوند. فریم ورک Fluent NHibernate با ایجاد کلاسهای کمکی برای این امر، به شدت سبب ساده سازی انجام آن شده است. در این مثال، نوع دیتابیس به SQLite و در حالت دیتابیس در حافظه (in memory)، تنظیم شده است (برای انجام امور آزمون واحد با سرعت بالا).
جهت اجرای هر دستوری در NHibernate نیاز به یک سشن میباشد. برای تعریف شیء سشن، نه تنها نیاز به مشخص سازی نوع و حالت دیتابیس مورد استفاده داریم، بلکه نیاز است تا وهلهای از کلاس استاندارد PersistanceModel را نیز جهت مشخص سازی کلاس نگاشت مورد استفاده مشخص نمائیم. برای این منظور کلاس TestModel فوق تعریف شده است تا این نگاشت را از اسمبلی مربوطه بخواند و مورد استفاده قرار دهد (بر پایی اولیه این مراحل شاید در ابتدای امر کمی زمانبر باشد اما در نهایت یک پروسه استاندارد است). توسط این کلاس به سیستم اعلام خواهیم کرد که اطلاعات نگاشت را باید از کدام کلاس دریافت کند.
تا اینجای کار شیء SessionSource را با معرفی نوع دیتابیس و همچنین محل دریافت اطلاعات نگاشت اشیاء معرفی کردیم. در دو سطر بعدی متد SetupContext کلاس FixtureBase ، ابتدا یک سشن را از این منبع سشن تهیه میکنیم. شیء منبع سشن در این فریم ورک در حقیقت یک factory object است (الگوهای طراحی برنامه نویسی شیءگرا) که امکان دسترسی به انواع و اقسام دیتابیسها را فراهم میسازد. برای مثال اگر روزی نیاز بود از دیتابیس اس کیوال سرور استفاده شود، میتوان از کلاس MsSqlConfiguration بجای SQLiteConfiguration استفاده کرد و همینطور الی آخر.
در ادامه توسط شیء SessionSource کار ساخت database schema را نیز به صورت پویا انجام خواهیم داد. بله، همانطور که متوجه شدهاید، کار ساخت database schema نیز به صورت پویا توسط فریم ورک NHibernate با توجه به اطلاعات کلاسهای نگاشت، صورت خواهد گرفت.
این مراحل، نحوه ایجاد و بر پایی یک آزمایشگاه آزمون واحد فریم ورک Fluent NHibernate را مشخص ساخته و در پروژههای شما میتوانند به کرات مورد استفاده قرار گیرند.
در ادامه اگر آزمون واحد را اجرا نمائیم (متد can_correctly_map_customer در کلاس CustomerMapping_Fixture)، نتیجه باید شبیه به شکل زیر باشد:
توسط متد CheckProperty کلاس PersistenceSpecification ، امکان بررسی نگاشت تهیه شده میسر است. اولین پارامتر آن، یک lambda expression خاصیت مورد نظر جهت بررسی است و دومین آرگومان آن، مقداری است که در حین آزمون به خاصیت تعریف شده انتساب داده میشود.
نکته:
شاید سؤال بپرسید که در تابع can_correctly_map_customer عملا چه اتفاقاتی رخ داده است؟ برای بررسی آن در متد SetupContext کلاس FixtureBase ، اولین سطر آنرا به صورت زیر تغییر دهید تا عبارات SQL نهایی تولید شده را نیز بتوانیم در حین عملیات تست مشاهده نمائیم:
var cfg = Fluently.Configure().Database(SQLiteConfiguration.Standard.ShowSql().InMemory);
مطابق متد تست فوق، عبارات تولید شده به شرح زیر هستند:
NHibernate: select next_hi from hibernate_unique_key
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 2, @p1 = 1
NHibernate: INSERT INTO "Customer" (FirstName, LastName, AddressLine1, AddressLine2, PostalCode, City, CountryCode, Id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7);@p0 = 'Vahid', @p1 = 'Nasiri', @p2 = 'Addr1', @p3 = 'Addr2', @p4 = '1234', @p5 = 'Tehran', @p6 = 'IR', @p7 = 1001
NHibernate: SELECT customer0_.Id as Id0_0_, customer0_.FirstName as FirstName0_0_, customer0_.LastName as LastName0_0_, customer0_.AddressLine1 as AddressL4_0_0_, customer0_.AddressLine2 as AddressL5_0_0_, customer0_.PostalCode as PostalCode0_0_, customer0_.City as City0_0_, customer0_.CountryCode as CountryC8_0_0_ FROM "Customer" customer0_ WHERE customer0_.Id=@p0;@p0 = 1001
نکته جالب این عبارات، استفاده از کوئریهای پارامتری است به صورت پیش فرض که در نهایت سبب بالا رفتن امنیت بیشتر برنامه (یکی از راههای جلوگیری از تزریق اس کیوال در ADO.Net که در نهایت توسط تمامی این فریم ورکها در پشت صحنه مورد استفاده قرار خواهند گرفت) و همچنین سبب بکار افتادن سیستمهای کش دیتابیسهای پیشرفته مانند اس کیوال سرور میشوند (execution plan کوئریهای پارامتری در اس کیوال سرور جهت بالا رفتن کارآیی سیستم کش میشوند و اهمیتی هم ندارد که حتما رویه ذخیره شده باشند یا خیر).
ادامه دارد ...
مطالب
Html Encoding
.
.
مقدمه
در دنیای وب دو انکدینگ معروف داریم: Url Encoding و Html Encoding. در هر کدام از این انکدینگها یک عملیات کلی صورت میگیرد: تبدیل کاراکترهای غیرمجاز به عبارات معادل مجاز.
Url Encoding همانطور که از نامش پیداست روشی برای کدکردن Url هاست. مثل عبارت کدشده زیر:
Hello%20world%20,%20hi
درواقع کاراکتر مشخصکننده رشتهای که Url Encoding احتمالا در آن اعمال شده است، همان کاراکتر % است. بحث درباره این نوع انکدینگ کمی مفصل است که خود مطلب جداگانهای میطلبد. (اطلاعات بیشتر)
Html Encoding نیز با توجه به نامش برای انکدینگ عبارات HTML استفاده میشود. مثلا عبارت زیر را درنظر بگیرید:
<html>encoding</html>
این عبارت پس از اعمال عملیات Html Encoding به صورت زیر در خواهد آمد:
<html>encoding</html>
میبینید که در اینجا کاراکترهای > و < به صورت عبارات ;lt& و ;gt& در آمدهاند. شرح کاملی درباره این عبارات معادل (که اصطلاحا به آنها character entity میگویند) در اینجا آورده شده است.
در حالت کلی Html Encoding شامل کدکردن 5 کاراکتر زیر است:
.
کاراکتر | عبارت معادل | توضیحات |
> | > | |
< | < | |
" | " | |
' | ' | یا ;apos& به غیر از IE |
& | & |
نکته: در برخی استانداردها (بیشتر برای XML) برای کاراکتر ' از عبارت ;apos& استفاده میشود. این عبارت جایگزین به غیر از IE در بقیه مرورگرها درست کار میکند.
این کاراکترها درواقع از عناصر اصلی تشکیلدهنده ساختار Html هستند، بنابراین وجود آنها درون یک متن میتواند در روند رندر صفحات html اختلال ایجاد کند. بنابراین با استفاده از Html Encoding و تبدیل این کاراکترها به معادلشان (عباراتی که مرورگرها آنها را میشناسند)، میتوان از نمایش درست این کاراکترها مطمئن شد. البته یکی دیگر از دلایل مهم اعمال این انکدینگ، افزایش امنیت و جلوگیری از حملات XSS است.
فرمت این عبارات معادل به صورت ;entity_name& است. به کل این عبارت اصطلاحا Character Entity گفته میشود. این عبارات با کاراکتر & شروع شده و به یک کاراکتر ; ختم میشوند. کلمه میان این دو کاراکتر نیز عبارت جایگزین (یا همان entity name) هر یک از این کاراکترهاست که در لینک بالا به همراه بسیاری دیگر از کاراکترها اشاره شده است (^).
روش دیگری نیز برای کدکردن کاراکترها با فرمت ;entity_number#& وجود دارد. این entity_number درواقع کد کاراکتر مربوطه در جدول کاراکترست جاری مرورگر است. معمولا این کدها منطبق بر جدول ASCII هستند. برای کاراکترهای خارج از جدول اسکی هم از سایر جداول (مثلا یونیکد) استفاده میشود. عملیات انکدینگ برای کاراکترهای با کد 160 تا 255 (براساس استاندارد ISO-8859-1) با این روش انجام میشود (^). اطلاعات بیشتر راجع به این کدها در اینجا آورده شده است.
خوشبختانه در سمت سرور، در داتنت روشهای گوناگون و قابل اطمینانی برای اعمال این انکدینگ وجود دارد. اما متاسفانه در سمت کلاینت چنین امکاناتی اصلا فراهم نیست و برنامه نویسان خود باید دست به کار شوند. ازآنجاکه امروزه قسمتهای بیشتری از اپلیکیشنهای تحت وب در سمت کلاینت پیاده میشوند و کتابخانههای سمت کلاینت روز به روز پرطرفدارتر میشوند وجود نمونههای مشابه از این متدها در سمت کلاینت میتواند بسیار مفید باشد.
بنابراین تمرکز اصلی ادامه این مطلب بیشتر بر نحوه اعمال این انکدینگ در سمت کلاینت با استفاده از زبان جاوا اسکریپت است.
.
.
Html Encoding در داتنت
در داتنت متدهای متعددی برای اعمال Html Encoding وجود دارد. برخی از آنها صرفا برای اسناد HTML طراحی شدهاند و برخی دیگر یک پیادهسازی کلی دارند و بعضی نیز برای فایلهای XML ارائه شدهاند. این متدها عبارتند از:
- متد System.Security.SecurityElement.Escape: این متد بیشتر برای اعمال این انکدینگ در XML بهکار میرود. در این متد 5 کاراکتر اشاره شده در بالا به عبارات معادل انکد میشوند. البته برای کاراکتر ' از عبارت ;apos& استفاده میشود.
- متدهای موجود در System.Net.WebUtility: متدهای HtmlEncode و HtmlDecode موجود در این کلاس عملیات انکدینگ را انجام میدهند. این کلاس از داتنت 4 اضافه شده است.
- متدهای کلاس System.Web.HttpUtility: در این کلاس از متدهای موجود در کلاس System.Web.Util.HttpEncoder استفاده میشود. در پیادهسازی پیشفرض، متدهای این کلاس از متدهای موجود در کلاس WebUtility استفاده میکنند. البته میتوان با فراهم کردن یک Encoder سفارشی و تنظیم آن در فایل کانفیگ (خاصیت encoderType در قسمت HttpRuntime) این رفتار را تغییر داد. دلیل اصلی جابجایی مکان پیادهسازی این متدها از دات نت 4 به بعد نیز به همین دلیل است. (اطلاعات بیشتر ^ و ^).
- متدهای موجود در System.Web.HttpServerUtility: متدهای HtmlEncode و HtmlDecode موجود در این کلاس مستقیما از متدهای موجود در کلاس HttpUtility استفاده میکنند. خاصیت Server موجود در HttpContext یا در کلاس Page از نوع این کلاس است.
- متدهای موجود در کلاس System.Web.Security.AntiXss.AntiXssEncoder: این کلاس از دات نت 4.5 اضافه شده است. همانطور که از نام این کلاس بر میآید، از HttpEncoder مشتق شده است که در متدهای مرتبط با html encoding تغییراتی در آن اعمال شده است. متدهای این کلاس برای امنیت بیشتر به جای استفاده از Black List از یک White List استفاده میکنند.
درحال حاضر بهترین گزینه موجود برای عملیات انکدینگ، متدهای موجود در کلاس WebUtility هستند. ازآنجاکه این کلاس در فضای System.Net و در کتابخانه System.dll قرار دارد (کتابخانهای که معمولا برای تمام برنامههای داتنتی نیاز است)، بنابراین بارگذاری آن در برنامه نیز بار اضافی بر حافظه تحمیل نمیکند.
پیادهسازی عملیات HtmlEncode کار سختی نیست. مثلا میتوان برای سادگی از متد Replace استفاده کرد. اما برای رشتههای طولانی این متد کارایی مناسبی ندارد. به همین دلیل در تمام پیادهسازیها، معمولا از یک حلقه بر روی تمام کاراکترهای رشته موردنظر برای یافتن کاراکترهای غیرمجاز استفاده میشود. در کدهای متدهای موجود، برای افزایش سرعت حتی از اشارهگر و کدهای unsafe نیز استفاده شده است.
برای افزایش کارایی در تولید رشته نهایی تبدیلشده، بهتر است از یک StringBuilder استفاده شود. در پیادهسازیهای متدهای بالا برای اینکار معمولا از یک TextWriter استفاده میشود. TextWriterهای موجود از کلاس StrigBuilder برای دستکاری رشتهها استفاده میکنند.
صرفا جهت آشنایی بیشتر، پیادهسازی خلاصهشده متد HtmlEncode در کلاس WebUtility در زیر آورده شده است:
public static unsafe void HtmlEncode(string value, TextWriter output) { int index = IndexOfHtmlEncodingChars(value, 0); if (index == -1) { output.Write(value); return; } int cch = value.Length - index; fixed (char* str = value) { char* pch = str; while (index-- > 0) { output.Write(*pch++); } while (cch-- > 0) { char ch = *pch++; if (ch <= '>') { switch (ch) { case '<': output.Write("<"); break; case '>': output.Write(">"); break; case '"': output.Write("""); break; case '\'': output.Write("'"); break; case '&': output.Write("&"); break; default: output.Write(ch); break; } } else if (ch >= 160 && ch < 256) { // The seemingly arbitrary 160 comes from RFC output.Write("&#"); output.Write(((int)ch).ToString(NumberFormatInfo.InvariantInfo)); output.Write(';'); } else { output.Write(ch); } } } } private static unsafe int IndexOfHtmlEncodingChars(string s, int startPos) { int cch = s.Length - startPos; fixed (char* str = s) { for (char* pch = &str[startPos]; cch > 0; pch++, cch--) { char ch = *pch; if (ch <= '>') { switch (ch) { case '<': case '>': case '"': case '\'': case '&': return s.Length - cch; } } else if (ch >= 160 && ch < 256) { return s.Length - cch; } } } return -1; }
در ابتدا بررسی میشود که آیا اصلا متن ورودی حاوی کاراکترهای غیرمجاز است یا خیر. درصورت عدم وجود چنین کاراکترهایی، کار متد با برگشت خود متن ورودی پایان مییابد. درغیراینصورت عملیات انکدینگ آغاز میشود.
همانطور که میبینید عملیات انکدینگ برای 5 کاراکتر اشاره شده به صورت جداگانه انجام میشود و برای کاراکترهای با کد 160 تا 255 (با توجه به توضیحات موجود در مقدمه) نیز با استاندارد ;code#& عملیات تبدیل انجام میشود.در سمت دیگر، پیادهسازی بهینه متد HtmlDecode چندان ساده نیست. چون به جای یافتن یک کاراکتر غیرمجاز باید به دنبال عبارات چند کاراکتری معادل گشت که کاری نسبتا پیچیده است.
اطلاعات و پیادهسازی نسبتا کاملی درباره Html Encoding در سمت سرور در اینجا قابل مشاهده است.
نکته: درصورت نیاز به کدکردن سایر کاراکترها (مثلا کاراکترهای یونیکد) پیادهسازیهای موجود کارا نخواهند بود. بنابراین باید encoder سفارشی خود را تهیه کنید. مثلا میتوانید شرط دوم در بررسی کد کاراکترها را بردارید (منظور قسمت ch < 256) که در اینصورت متد شما محدوده وسیعی را پوشش میدهد. اما دقت کنید که با این تغییر متدی سفارشی برای عملیات decode نیز باید تهیه کنید!
.
.
Html Encoding در جاوا اسکریپت
برای انجام عملیات Url Encoding در جاوا اسکریپت چند متد توکار وجود دارد، که فرایند کلی عملیات همه آنها تقریبا یکسان است. اما متاسفانه برای انجام عملیات Html Encoding متدی در جاوا اسکریپت وجود ندارد. بنابراین متدهای مربوطه باید توسط خود برنامهنویسان پیادهسازی شوند.
یک روش برای اینکار استفاده از لیست اشارهشده در بالا و انجام عملیات replace برای تمام این کاراکترهاست (5 کاراکتر اصلی و درصورت نیاز سایر کاراکترها). این کار میتواند کمی سخت باشد و درواقع پیادهسازی چنین متدی نسبتا مشکل نیز هست (مخصوصا عملیات decode).
اما خوشبختانه امکانی در اسناد html وجود دارد که این کار (مخصوصا Decode کردن) را آسان میکند.
این روش جالب برای انجام عملیات Html Encoding در جاوا اسکریپت، استفاده از یک قابلیت توکار در مرورگرهاست. عناصر DOM (مانند div) دو خاصیت innerText و innerHTML دارند که مرورگرها با توجه به مقادیر تنظیمشده برای هر یک، عملیات coding و decoding مربوطه را به صورت کاملا خودکار انجام داده و مقدار خاصیت دیگر را بهروزرسانی میکنند (دقت کنید که در این دو پراپرتی، کلمه HTML کاملا با حروف بزرگ است، برخلاف Text که تنها حرف اول آن بزرگ است).
برای روشنتر شدن موضوع به مثال زیر برای عملیات encode توجه کنید:
<div id="log"></div> <script type="text/javascript"> var element = document.getElementById('log'); element.innerText = '<html> encoding </html>'; console.log(element.innerHTML); </script>
<html> encoding </html>
عکس این عملیات یعنی decoding نیز با استفاده از کدی مثل زیر امکانپذیر است:
<div id="log"> </div> <script type="text/javascript"> var element = document.getElementById('log'); element.innerHTML = "<html> encoding </html>"; console.log(element.innerText); </script>
<html> encoding </html>
..
.
متد htmlEncode
برای پیادهسازی این متد برای حالت استفاده مستقیم داریم:
String.htmlEncode = function (s) { var el = document.createElement("div"); el.innerText = s || ''; return el.innerHTML; };
در اینجا با استفاده از متد createElement شی document یک المان DOM (در اینجا div) ایجاد شده و سپس با توجه به توضیحات بالا خاصیت innerText آن به مقدار ورودی تنظیم میشود. استفاده از عبارت '' || s در اینجا برای جلوگیری از برگشت عبارات ناخواسته (مثل undefined یا null) برای ورودیهای غیرمجاز است. درنهایت خاصیت innerHTML این المان به عنوان رشته انکدشده برگشت داده میشود.
console.log(String.htmlEncode("<html>")); //result: <html>
و برای حالت استفاده از خاصیت prototype داریم:
String.prototype.htmlEncode = function () { var el = document.createElement("div"); el.innerText = this.toString(); return el.innerHTML; };
console.log("<html>".htmlEncode()); //result: <html>
.
.
متد htmlDecode
با استفاده از مطالب اشارهشده در بالا، پیادهسازی این متد به صورت زیر است:
String.htmlDecode = function (s) { var el = document.createElement("div"); el.innerHTML = s || ''; return el.innerText; };
String.prototype.htmlDecode = function () { var el = document.createElement("div"); el.innerHTML = this.toString(); return el.innerText; };
نحوه استفاده از این متدها هم به صورت زیر است:
console.log(String.htmlDecode("<html>")); console.log("<html>".htmlDecode());
.
.
پیادهسازی با استفاده از jQuery
درصورت در دسترس بودن کتابخانه jQuery، کار پیادهسازی این متدها بسیار سادهتر خواهد شد. برای اینکار میتوان از متدهای زیر استفاده کرد:
.
- متد htmlEncode:
String.htmlEncode = function (s) { return $('<div/>').text(value).html(); }; String.prototype.htmlEncode = function () { return $('<div/>').text(this.toString()).html(); };
- متد htmlDecode:
String.htmlDecode = function (s) { return $('<div/>').html(s).text(); }; String.prototype.htmlDecode = function () { return $('<div/>').html(this.toString()).text(); };
.
.
نکات پایانی
1. با اینکه به نظر میرسد در متدهای ارائه شده در بالا، بین نسخههای معمولی و نسخه مخصوص jQuery تفاوتی وجود ندارد اما تست زیر نشان میدهد که نکات ریزی باعث بهوجود آمدن برخی تفاوتها میشود. رشته زیر را درنظر بگیرید:
var value = "a \n b";
با استفاده از متد htmlEncode معمولی نشان داده شده در بالا، عبارت انکدشده رشته فوق به صورت زیر خواهد بود:
"a <br> b"
میبینید که به صورت هوشمندانهای! مقدار n\ به تگ <br> انکد شده است. اما اگر با استفاده از متد نوشته شده با jQuery سعی به انکدکردن این رشته کنیم، میبینیم که مقدار n\ بدین صورت انکد نمیشود! حال کدام روش درست و استاندارد است؟
در ابتدای این مطلب هم اشاره شده بود که Html Encoding برای کدکردن یکسری کاراکتر غیرمجاز در متون موجود در صفحات HTML بکار میرود و معمولا همان 5 کاراکتر اشارهشده در بالا به عنوان کاراکترهای اصلی غیرمجاز به حساب میآیند. کاراکتر n\ از این نوع کاراکترها محسوب نمیشود. همچنین ازآنجاکه عملیات عکس این تبدیل در Decode مربوطه صورت نمیگیرد، تبدیل این کاراکتر به معادلش در html اصلا کاری منطقی نیست و باعث خراب شدن متن موردنظر میشود.
با استفاده از متدهای HtmlEncode موجود در کلاسهای دات نت (WebUtility و HtmlUtility که در بالا به آنها اشاره شده بود) عملیات انکدینگ برای این رشته تکرار شد و نتیجه حاصله نشان داد که عبارت n\ در خروجی این متدها نیز انکد نمیشود. بنابراین متد نوشته شده با استفاده از jQuery خروجیهای استانداردتری ارائه میدهد.
با کمی تحقیق و بررسی کدهای jQuery مشخص شد که دلیل این تفاوت، در استفاده از متد createTextNode از شی document در متد ()text است. بنابراین برای بهبود متد htmlEncode اولیه داریم:
String.htmlEncode = function (s) { var el = document.createElement("div"); var txt = document.createTextNode(s); el.appendChild(txt); return el.innerHTML; };
.
.
2. نکته مهم دیگری که باید بدان توجه داشت برقراری اصل مهم زیر در عملیات انکدینگ است:
String.htmlDecode(String.htmlEncode(myString)) === myString;
var myString = "<HTML>"; String.htmlDecode(String.htmlEncode(myString)) === myString; // result: true // -------------------------------------------------------------------------- myString = "<اچ تی ام ال>"; String.htmlDecode(String.htmlEncode(myString)) === myString; // result: true
myString = "a \r b"; String.htmlDecode(String.htmlEncode(myString)) === myString; // result: false
پس از کمی تحقیق و بررسی بیشتر مشخص شد که مرورگرها در تبدیل کاراکترها، کاراکتر carriage return (یا CR یا همان r\ با کد اسکی 13 یا 0D) را تبدیل به کاراکتر line feed (یا LF یا n\ با کد اسکی 10 یا 0A) میکنند. برای آزمایش این نکته میتوانید از سه خط زیر استفاده کنید:
console.log(escape(String.htmlDecode('\r'))); // result: %0A : it is url encode of character '\n' console.log(escape(String.htmlDecode('\n'))); // result: %0A console.log(escape(String.htmlDecode('\r\n'))); // result: %0A
با بررسی بیشتر مشخص شد که این تبدیل به محض مقداردهی به یکی از خاصیتهای یک عنصر DOM صورت میگیرد. برای مثال کد زیر را در مرورگرهای مختلف امتحان کنید:
var el = document.createElement('div'); el.innerText = '\r'; console.log(escape(el.innerText)); // result: %0A el.innerHTML = '\r'; console.log(escape(el.innerHTML)); // result: %0A console.log(escape('\r')); // result: %0D
با بررسی هایی که من کردم دلیل و یا راهحلی برای این مشکل پیدا نکردم!
بنابراین در استفاده از این متدها باید این نکته را مدنظر قرار داد. ازآنجاکه این مشکل حالتی به خصوص دارد نمیتوان راهحلی کلی برای آن ارائه داد. پس برای موقعیتهای گوناگون با توجه به زوایای روشنشده از این مشکل باید به دنبال راهحل مناسب بود.
البته ممکن است این اشکال درمورد کاراکترهای دیگری هم وجود داشته باشد که من به آن برخورد نکرده باشم (با درنظر گرفتن تفاوت میان مرورگرهای مختلف ممکن است پیچیدهتر هم باشد).
نکته: ازآنجاکه برای رفع این مشکل، پیادهسازی متد htmlDecode به این کاملی، با عدم استفاده از ویژگی پراپرتیهای innerHTML و innerText، کاری نسبتا سخت و پیچیده و طولانی است، بنابراین در بیشتر حالات میتوان از این مشکل صرفنظر کرد! به همین دلیل در اینجا نیز متد دیگری برای رفع این مشکل ارائه نمیشود!
.
.
3. یک مشکل دیگر که این متدها دارند این است که متاسفانه در متد htmlEncode، از 5 کاراکتر معروف بالا، کاراکترهای ' و " در این متدها اصلا تبدیل نمیشوند. همچنین سایر کاراکترهای عنواندار یا کاراکترهای خارج از جدول ASCII (مثلا کاراکترهای با کد 160 تا 255 یا کاراکترهای یونیکد) نیز که معمولا انکد میشوند در این متد تغییری نمیکنند و به همان صورت برگشت داده میشوند.
هرچند متد htmlDecode نشان داده شده در این مطلب، بهدرستی تمامی عبارات معادل (حتی عبارات معادل غیر از 5 کاراکتر نشان داده شده در بالا با هر دو استاندارد ;character-entity& و ;code#&) را تبدیل کرده و کاراکتر درست را برمیگرداند.
برای اصلاح این مشکل میتوان متد htmlEncode را کاملا به صورت دستی و مستقیم نوشت و اعمال انکدینگهای موردنیاز را با استفاده یک حلقه روی تمام کاراکترها متن موردنظر انجام داد. چیزی شبیه به کد زیر:
String.htmlEncode = function (text) { text = text || ''; var encoded = ''; for (var i = 0; i < text.length; i++) { var c = text[i]; switch (c) { case '<': encoded += '<'; break; case '>': encoded += '>'; break; case '&': encoded += '&'; break; case '"': encoded += '"'; break; case "'": encoded += '''; break; default: // the upper limit can be removed to support more chars... var code = c.charCodeAt(); if (code >= 160 & code < 256) encoded += '&#' + code + ';'; else encoded += c; } } return encoded; };
.
.
کتابخانههای موجود
هرچند توضیحات ارائه شده در این مطلب کافی هستند، اما صرفا برای آشنایی با سایر کتابخانههای موجود، روشهای استفادهشده در آنها و نقایص و مزایای آنها این قسمت اضافه شده است.
.
Prototype: این کتابخانه شامل مجموعهای از متدهای کمکی برای راحتی کار در سمت کلاینت است. برای عملیات html encoding دو متد escapeHTML و unescapeHTML دارد که به صورت زیر پیاده شدهاند:
function escapeHTML() { return this.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); } function unescapeHTML() { return this.stripTags().replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); }
همانطور که میبینید در این متدها از replace استفاده شده است که برای متنهای طولانی کندتر از روشهای نشان دادهشده در این مطلب است. همچنین عملیات انکد و دیکد را تنها برای 3 کاراکتر < و > و & انجام میدهد که نقص بزرگی محسوب میشود.
.
jQuery.string: این پلاگین حاوی چند متد برای کار با رشتههاست که یکی از این متدها با نام htmlspecialchars مخصوص عملیات انکدینگ است. در این متد تنها همان 5 کاراکتر اصلی تبدیل میشوند. متاسفانه متدی برای decode در این پلاگین وجود ندارد. پیادهسازی خلاصهشده این کتابخانه تنها برای نمایش نحوه عملکرد متد فوق به صورت زیر است:
var andExp = /&/g, htmlExp = [/(<|>|")/g, /(<|>|')/g, /(<|>|'|")/g], htmlCharMap = { '<': '<', '>': '>', "'": ''', '"': '"' }, htmlReplace = function (all, $1) { return htmlCharMap[$1]; }; $.extend({ // convert special html characters htmlspecialchars: function (string, quot) { return string.replace(andExp, '&').replace(htmlExp[quot || 0], htmlReplace); } });
$.htmlspecialchars("<div>");
string.$: پلاگین دیگری برای jQuery که عملیات مربوط به رشتهها را دربر دارد. در این پلاگین برای عملیات انکدینگ دو متد escapeHTML و unescapeHTML به صورت زیر تعریف شدهاند:
this.escapeHTML = function (s) { this.str = this.s(s) .split('&').join('&') .split('<').join('<') .split('>').join('>'); return this; }; this.unescapeHTML = function (s) { this.str = this.stripTags(this.s(s)).str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); return this; };
همانطور که میبنید در متد encode این پلاگین از یک روش جالب اما به نسبت ناکارآمد در رشتههای طولانی، برای استخراج کاراکترهای غیرمجاز استفاده شده است. در این متدها هم تنها 3 کاراکتر & و < و > انکد و دیکد میشوند.
.
encoder.js: کتابخانه نسبتا کاملی برای عملیات انکدینگ رشتهها در سمت کلاینت. این کتابخانه علاوه بر encode و decode رشتهها متدهایی برای تبدیل html entityها به فرمت عددیشان و برعکس، حذف کاراکترهای یونیکد، بررسی اینکه رشته ورودی شامل کاراکترهای انکد شده است، جلوگیری از انکدینک مجدد یک رشته و ... نیز دارد.
. .
htmlEncode: این متد پیادهسازی کاملی برای اجرای عملیات Html Encode دارد و محدوده وسیعی از کاراکترها را نیز تبدیل میکند. مشاهده عملیات موجود در این متد برای آشنایی با مطالب ظریفتر پیشنهاد میشود.
.
.
منابع برای مطالعه بیشتر
در این قسمت به تکمیل مثالی که در قسمت قبل زده شد پرداخته میشود و همچنین کنترلهای Foreach , Try Catch نیز بررسی خواهند شد.
نکته : از کلاس Code Activity برای ارسال و دریافت مقادیر به درون Workflow استفاده میشود.
در ابتدا دو کلاس به نامهای ItemInfo و OutOfStockException را به برنامه اضافه میکنیم. کلاس اول برای ذخیره سازی مشخصات هر سفارش و کلاس دیگر برای مدیریت خطاها میباشد.
public class ItemInfo { public string ItemCode { get; set; } public string Description { get; set; } public decimal Price { get; set; } } public class OutOfStockException : Exception { public OutOfStockException() : base() { } public OutOfStockException(string message) : base(message) { } }
در Workflow مورد نظر که به نام OrderWF.xaml میباشد٬ پس از کنترل Assign که برای صفر کردن مقدار متغیر TotalAmount از آن استفاده میشود٬ یک کنترل ForEach را به Flow جاری اضافه میکنیم. این کنترل دارای دو خاصیت به نامهای Type Arguments و Values میباشد. اولین خاصیت که مقدار پیش فرض آن، مقدار عددی Int32 است٬ برای مشخص کردن نوع متغییر حلقه و دیگری برای مشخص کردن نوع منبع داده حلقه تعریف شدهاند.
همانطور که در شکل بالا مشخص میباشد٬ Type Arguments حلقه برابر با کلاس OrderItem میباشد. Values هم برابر با OrderInfo.Items است. از این جهت نوع حلقه را از جنس کلاس OrderItem مشخص کردهایم تا کنترل بر روی مقادیر Items اجرا شود (لیستی از کلاس مورد نظر).
حال همانند شکل بالا، در قسمت Body کنترل ForEach، یک کنترل Sequence را ایجاد کرده و سپس برای اینکه کنترل LookupItem را ایجاد کنیم٬ ابتدا باید یک Code Activity را به پروژه اضافه کنیم. به همین منظور پروژه جاری را انتخاب کرده و یک Code Activity به آن اضافه و نام آن را LookupItem میگذاریم. سپس کد زیر را به آن اضافه میکنیم:
public sealed class LookupItem : CodeActivity { // Define an activity input argument of type string public InArgument<string> ItemCode { get; set; } public OutArgument<ItemInfo> Item { get; set; } // If your activity returns a value, derive from CodeActivity<TResult> // and return the value from the Execute method. protected override void Execute(CodeActivityContext context) { // Obtain the runtime value of the Text input argument ItemInfo i = new ItemInfo(); i.ItemCode = context.GetValue<string>(ItemCode); switch (i.ItemCode) { case "12345": i.Description = "Widget"; i.Price = (decimal)10.0; break; case "12346": i.Description = "Gadget"; i.Price = (decimal)15.0; break; case "12347": i.Description = "Super Gadget"; i.Price = (decimal)25.0; break; } context.SetValue(this.Item, i); } }
در این کد، دو متغییر تعریف شدهاند؛ یکی از نوع رشته بوده و از طریق آن، دستور Switch تصمیم میگیرد که کلاس ItemInfo را با چه مقادیری پرکند. متغییر دیگر از نوع کلاس ItemInfo میباشد و برای گرفتن مقدار کلاس از دستور Switch تعریف شده است.
حال برای اینکه بتوانیم از Code Activity مورد نظر استفاده کنیم٬ ابتدا باید پروژه را یکبار Build کنیم. اکنون در قسمت Toolbox یک٬ Tab ایی به نام پروژه ایجاد شده است و در آن یک کنترل به نام LookupItem موجود میباشد. آن را گرفته و به درون Sequence انتقال میدهیم.
سپس برای مقدار دادن به متغیرهای تعریف شده در Code Activity، کنترل LookupItem را انتخاب کرده و در قسمت Properties به خصوصیت ItemCode، کد زیر را اضافه میکنیم:
item.ItemCode
نکته : از کلاس Code Activity برای ارسال و دریافت مقادیر به درون Workflow استفاده میشود.
Try Catch
از این کنترل برای مدیریت خطاها استفاده میشود.
ابتدا یک کنترل Try Catch را به Workflow اضافه کرده، مانند شکل زیر:
در بدنه Try میتوان از کنترلهای مورد نظر استفاده کنیم و همانطور که در شکل بالا مشخص است٬ از کنترل Throw برای ایجاد خطا استفاده شدهاست. کنترل جاری را انتخاب کرده و از قسمت Properties در خاصیت Exception کد زیر را اضافه میکنیم:
new OutOfStockException("Item Code"+item.ItemCode)
این دستور باعث ایجاد یک خطا از نوع کلاس OutOfStockException میشود. برای کنترل خطای مورد نظر در قسمت Catches مانند شکل زیر عمل میکنیم.
در مورد موارد مطرحی که باشه یا نه به این راحتی نمیشه نظر داد و باید کدها رو بررسی کرد ، یا حداقل من نمیشناسم که صرفا فانکشنال باشه قطعا اگه سورس کد هایی که با زبان haskell یا f# یا سایر زبان هایی که پارادایم اونها فانکشنال هست رو نگاه کنیم میتونیم موارد زیادی رو پیدا کنیم
این که دغدغه کسی یادگیری شی گرایی باشه هیچ تعارضی با فانکشنال نداره ، من عرض کردم این دو تا مقابل هم نیستن اصلا. و میتونن همدیگه رو کامل کنند.
در مورد این قسمت صحبت شما : آیا بسیاری از قوانین برنامه نویسی فانکشنال، مواردی نیست که بهتره در برنامه نویسی شی گرا هم تا حد امکان مد نظر داشته باشیم؟ کاملا باهاتون هم نظر هستم که این مفاهیم چه در شی گرایی چه به صورت جدا مفاهیم درستی هستن که رعایت کردنش میتونه کیفیت کدهای ما رو ببره بالا و جلوی یه سری مشکلات احتمالی رو بگیره
این که دغدغه کسی یادگیری شی گرایی باشه هیچ تعارضی با فانکشنال نداره ، من عرض کردم این دو تا مقابل هم نیستن اصلا. و میتونن همدیگه رو کامل کنند.
در مورد این قسمت صحبت شما : آیا بسیاری از قوانین برنامه نویسی فانکشنال، مواردی نیست که بهتره در برنامه نویسی شی گرا هم تا حد امکان مد نظر داشته باشیم؟ کاملا باهاتون هم نظر هستم که این مفاهیم چه در شی گرایی چه به صورت جدا مفاهیم درستی هستن که رعایت کردنش میتونه کیفیت کدهای ما رو ببره بالا و جلوی یه سری مشکلات احتمالی رو بگیره
در تکمیل این بحث هم بد نیست لایبرری زیر رو ببینید که یه سری اکستنشن برای سی شارپ که بتونید راحتتر قوانین فانکشنال رو پیاده کنید ببینید
اجرای کدهای اسمبلی
همانطور که قبلا ذکر کردیم یک اسمبلی شامل کدهای IL و متادیتا هاست. IL یک زبان غیر وابسته به معماری سی پی یو است که مایکروسافت پس از مشاورههای زیاد از طریق نویسندگان کامپایلر و زبانهای آکادمی و تجاری آن را ایجاد کرده است. IL یک زبان کاملا سطح بالا نسبت به زبانهای ماشین سی پی یو است. IL میتواند به انواع اشیاء دسترسی داشته و آنها را دستکاری نماید و شامل دستورالعمل هایی برای ایجاد و آماده سازی اشیاست. صدا زدن متدهای مجازی بر روی اشیاء و دستکاری المانهای یک آرایه به صورت مستقیم، از جمله کارهایی است که انجام میدهد. همچنین شامل دستوراتی برای صدور و کنترل استثناء هاست . شما میتوانید IL را به عنوان یک زبان ماشین شیء گرایی تصور کنید.
معمولا برنامه نویسها در یک زبان سطح بالا چون سی شارپ به نوشتن میپردازند و کمپایلر کد IL آنها را ایجاد میکند و این کد IL میتواند به صورت اسمبلی نوشته شود. به همین علت مایکروسافت ابزار ILASM.exe و برای دی اسمبل کردن ILDASM.exe را ارائه کرده است.
این را همیشه به یاد داشته باشید که زبانهای سطح بالا تنها به زیر قسمتی از قابلیتهای CLR دسترسی دارند؛ ولی در IL Assembly توسعه دهنده به تمامی قابلیتهای CLR دسترسی دارد. این انتخاب شما در زبان برنامه نویسی است که میخواهید تا چه حد به قابلیتهای CLR دسترسی داشته باشید. البته یکپارچه بودن محیط در CLR باعث پیوند خوردن کدها به یکدیگر میشود. برای مثال میتوانید قسمتی از یک پروژه که کار خواندن و نوشتن عملیات را به عهده دارد بر دوش #C قرار دهید و محاسبات امور مالی را به APL بسپارید.
برای اجرا شدن کدهای IL، ابتدا CLR باید بر اساس معماری سی پی یو کد ماشین را به دست آورد که وظیفهی تبدیل آن بر عهده Jit یا Just in Time است . شکل زیر نحوه انجام این کار را انجام میدهد:
همانطور که قبلا ذکر کردیم یک اسمبلی شامل کدهای IL و متادیتا هاست. IL یک زبان غیر وابسته به معماری سی پی یو است که مایکروسافت پس از مشاورههای زیاد از طریق نویسندگان کامپایلر و زبانهای آکادمی و تجاری آن را ایجاد کرده است. IL یک زبان کاملا سطح بالا نسبت به زبانهای ماشین سی پی یو است. IL میتواند به انواع اشیاء دسترسی داشته و آنها را دستکاری نماید و شامل دستورالعمل هایی برای ایجاد و آماده سازی اشیاست. صدا زدن متدهای مجازی بر روی اشیاء و دستکاری المانهای یک آرایه به صورت مستقیم، از جمله کارهایی است که انجام میدهد. همچنین شامل دستوراتی برای صدور و کنترل استثناء هاست . شما میتوانید IL را به عنوان یک زبان ماشین شیء گرایی تصور کنید.
معمولا برنامه نویسها در یک زبان سطح بالا چون سی شارپ به نوشتن میپردازند و کمپایلر کد IL آنها را ایجاد میکند و این کد IL میتواند به صورت اسمبلی نوشته شود. به همین علت مایکروسافت ابزار ILASM.exe و برای دی اسمبل کردن ILDASM.exe را ارائه کرده است.
این را همیشه به یاد داشته باشید که زبانهای سطح بالا تنها به زیر قسمتی از قابلیتهای CLR دسترسی دارند؛ ولی در IL Assembly توسعه دهنده به تمامی قابلیتهای CLR دسترسی دارد. این انتخاب شما در زبان برنامه نویسی است که میخواهید تا چه حد به قابلیتهای CLR دسترسی داشته باشید. البته یکپارچه بودن محیط در CLR باعث پیوند خوردن کدها به یکدیگر میشود. برای مثال میتوانید قسمتی از یک پروژه که کار خواندن و نوشتن عملیات را به عهده دارد بر دوش #C قرار دهید و محاسبات امور مالی را به APL بسپارید.
برای اجرا شدن کدهای IL، ابتدا CLR باید بر اساس معماری سی پی یو کد ماشین را به دست آورد که وظیفهی تبدیل آن بر عهده Jit یا Just in Time است . شکل زیر نحوه انجام این کار را انجام میدهد:
قبل از اجرای متد Main، ابتدا CLR به دنبال ارجاعاتی میگردد که در این متد استفاده شده است تا یک ساختار داده داخلی، برای ارجاعات این متد در حافظه تشکیل شود. در شکل بالا یک ارجاع وجود دارد و آن هم شیء کنسول است. این ساختار داده داخلی شامل یک مدخل ورودی (آدرس آغاز در حافظه) به ازای هر متد تعریف شده در نوع کنسول است. هر مدخل ورودی شامل آدرسی است که متدها در آنجا پیاده سازی شدهاند. موقعیکه این آماده سازی انجام میگیرد، آنها را به سمت یک تابع مستند نشده در خود CLR به نام Jit Compiler ارسال میکند.
موقعیکه کنسول اولین متدش مثلا WriteLine را فراخوانی میکند، کامپایلر جیت صدا زده میشود. تابع کامپایلر جیت مسئولیت تبدیل کدهای IL را به کدهای بومی آن پلتفرم، به عهده دارد. از آنجایی که عمل کامپایل در همان لحظه یا در جا اتفاق میافتد (Just in time)، عموم این کامپایر را Jitter یا Jit Compiler مینامند.
موقعیکه صدا زدن آن متد به سمت jit انجام شد، جیت متوجه میشود که چه متدی درخواست شده و نحوهی تعریف آن متد به چه صورتی است. جیت هم در متادیتای یک اسمبلی به جست و جو پرداخته و کدهای IL آن متد را دریافت میکند. سپس کدها را تایید و عملیات کامپایل به سمت کدهای بومی را آغاز میکند. در ادامه این کدهای بومی را در قطعهای از حافظه ذخیره میکند. سپس جیت به جایی بر میگردد که CLR از آنجا جیت را وارد کار کرده؛ یعنی مدخل ورودی متد writeline و سپس آدرس آن قطعه حافظه را که شامل کد بومی است، بجای آن قطعه که به کد IL اشاره میکند، جابجا میکند و کد بومی شده را اجرا و نهایتا به محدودهی main باز میگردد.
در شکل زیر مجددا همان متد صدا زده شده است. ولی از آنجا که قبلا کد کامپایل شده را به دست آوردیم، از همان استفاده میکنیم و دیگر تابع جیت را صدا نمیزنیم.
توجه داشته باشید، در متدهای چند ریختی که شکلهای متفاوتی از پارامترها را دارند، هر کدام کمپایل جداگانهای صورت میگیرد. یعنی برای متدهای زیر جیت برای هر کدام جداگانه فراخوانی میشود.
در مقالهی آینده عملکرد جیت را بیشتر مورد بررسی قرار میدهیم و در مورد دیباگ کردن و به نظرم برتری CLR را نسبت به زبانهای مدیریت نشده، بررسی میکنیم.
موقعیکه کنسول اولین متدش مثلا WriteLine را فراخوانی میکند، کامپایلر جیت صدا زده میشود. تابع کامپایلر جیت مسئولیت تبدیل کدهای IL را به کدهای بومی آن پلتفرم، به عهده دارد. از آنجایی که عمل کامپایل در همان لحظه یا در جا اتفاق میافتد (Just in time)، عموم این کامپایر را Jitter یا Jit Compiler مینامند.
موقعیکه صدا زدن آن متد به سمت jit انجام شد، جیت متوجه میشود که چه متدی درخواست شده و نحوهی تعریف آن متد به چه صورتی است. جیت هم در متادیتای یک اسمبلی به جست و جو پرداخته و کدهای IL آن متد را دریافت میکند. سپس کدها را تایید و عملیات کامپایل به سمت کدهای بومی را آغاز میکند. در ادامه این کدهای بومی را در قطعهای از حافظه ذخیره میکند. سپس جیت به جایی بر میگردد که CLR از آنجا جیت را وارد کار کرده؛ یعنی مدخل ورودی متد writeline و سپس آدرس آن قطعه حافظه را که شامل کد بومی است، بجای آن قطعه که به کد IL اشاره میکند، جابجا میکند و کد بومی شده را اجرا و نهایتا به محدودهی main باز میگردد.
در شکل زیر مجددا همان متد صدا زده شده است. ولی از آنجا که قبلا کد کامپایل شده را به دست آوردیم، از همان استفاده میکنیم و دیگر تابع جیت را صدا نمیزنیم.
توجه داشته باشید، در متدهای چند ریختی که شکلهای متفاوتی از پارامترها را دارند، هر کدام کمپایل جداگانهای صورت میگیرد. یعنی برای متدهای زیر جیت برای هر کدام جداگانه فراخوانی میشود.
WriteLine("Hello"); WriteLine();
در مقالهی آینده عملکرد جیت را بیشتر مورد بررسی قرار میدهیم و در مورد دیباگ کردن و به نظرم برتری CLR را نسبت به زبانهای مدیریت نشده، بررسی میکنیم.
اشتراکها
از #F به #C
اشتراکها
ادغام تیم #M با #C
یکی از تیمهای تحقیقاتی مایکروسافت که بر روی زبان #M کار میکرد با #C ادغام شدهاست. یکی از مقالات آنها که احتمالا در نگارش بعدی دات نت حضور خواهد داشت.
اشتراکها