Power of Reflection Emit
How to create a class with properties at run time
با TypeScript چه چیزهایی به دست خواهیم آورد؟
یک نکته مهم این است که این زبان به خوبی در Visual Studio پشتیبانی میشود و قابلیت Intellisense نوشتن برنامه به این زبان را دلپذیرتر خواهد کرد و از طرفی دیگر به نظر من یکی از مهمترین مزیت هایی که TypeScript در اختیار ما قرار میدهد این است که میتوانیم به صورت Syntax آشنای شی گرایی کد نویسی کنیم و خیلی راحتتر کدهای خود را سازمان دهی کرده و از نوشتن کدهای تکراری اجتناب کنیم.
یکی دیگر از مزیتهای مهم این زبان این است که این زبان از Static Typing به خوبی پشتیبانی میکند. این بدین معنی است که شما ابتدا باید متغیرها را تعریف کرده و نوع آنها را مشخص نمایید و هم چنین در هنگام پاس دادن مقادیر به پارامترهای توابع باید حتما به نوع داده ای آنها دقت داشته باشید چون کامپایلر بین انواع داده ای در TypeScript تمایز قایل است و در صورت رعایت نکردن این مورد شما با خطا مواجه خواهید شد. این تمایز قایل شدن باعث میشود که برنامه هایی خواناتر داشته باشیم از طرفی باعث میشود که خطا یابی و نوشتن تست برای برنامه راحتتر و تمیزتر باشد. بر خلاف JavaScript، در TypeScript(به دلیل پشتیبانی از شی گرایی) میتوانیم علاوه بر داشتن کلاس، اینترفیس نیز داشته باشیم و در حال حاضر مزایای استفاده از اینترفیس بر کسی پوشیده نیست.
به دلیل اینکه کدهای TypeScript ابتدا کامپایل شده و بعد تبدیل به کدهای JavaScript میشوند در نتیجه قبل از رسیدن به مرحله اجرای پروژه، ما از خطاهای موجود در کد خود مطلع خواهیم شد.
البته این نکته را نیز فراموش نخواهیم کرد که این زبان تازه متولد شده است(سال 2012 توسط Anders Hejlsberg) و همچنان در حال توسعه است و این در حال حاضر مهمترین عیب این زبان میتواند باشد چون هنوز به پختگی سایر زبانهای اسکریپتی در نیامده است.
در ذیل یک مثال کوچک به زبان TypeScript و JavaScript را برای مقایسه در خوانایی و راحتی کد نویسی قرار دادم:
TypeScript:
class Greeter { greeting: string; constructor (message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; } }
var Greeter = (function () { function Greeter(message) { this.greeting = message; } Greeter.prototype.greet = function () { return "Hello, " + this.greeting; }; return Greeter; })();
Program : یک برنامه TypeScript مجموعه ای از یک یا چند Source File است. این Source Fileها شامل کدهای پیاده سازی برنامه هستند ولی در خیلی موارد برای خوانایی بیشتر برنامه میتوان فقط تعاریف را در این فایلهای سورس قرار داد.
Module: ماژول در TypeScript شبیه به مفاهیم فضای نام یا namespace در دات نت است و میتواند شامل چندین کلاس یا اینترفیس باشد.
Class : مشابه به مفاهیم کلاس در دات نت است و دقیقا همان مفهوم را دارد. یک کلاس میتواند شامل چندین تابع و متغیر با سطوح دسترسی متفاوت باشد. در TypeScript مجاز به استفاده از کلمات کلیدی public و private نیز میباشید. یک کلاس در Typescript میتواند یک کلاس دیگر را توسعه دهد(ارث بری در دات نت) و چندین اینترفیس را پیاده سازی نماید.
Interface: یک اینترفیس فقط شامل تعاریف است و پیاده سازی در آن انجام نخواهد گرفت. یک اینترفیس میتواند چندین اینترفیس دیگر را توسعه دهد.
Function: معادل متد در دات نت است. میتواند پارامتر ورودی داشته باشد و در صورت نیاز یک مقدار را برگشت دهد.
Scope: دقیقا تمام مفاهیم مربوط به محدوده فضای نام و کلاس و متد در دات نت در این جا نیز صادق است.
آماده سازی Visual Studio برای شروع به کار
در ابتدا باید Template مربوطه به TypeScript را نصب کنید تا از طریف VS.Net بتوانیم به راحتی به این زبان کد نویسی کنیم. میتوانید فایل نصب را از اینجا دانلود کنید. بعد از نصب از قسمت Templateهای موجود گزینه Html Application With TypeScript را انتخاب کنید
یا از قسمت Add در پروژههای وب خود نظیر MVC گزینه TypeScript File را انتخاب نمایید.
در پست بعدی کد نویسی با این زبان را آغاز خواهیم کرد.
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
}
using System.Web.Mvc;
namespace MvcApplication2.Controllers
{
public class HomeController : Controller
{
[NonAction]
public string ShowData()
{
return "Text";
}
public ActionResult Index()
{
ViewBag.Message = string.Format("{0}/{1}/{2}",
RouteData.Values["controller"],
RouteData.Values["action"],
RouteData.Values["id"]);
return View();
}
public ActionResult Search(string data = "*")
{
// do something ...
return View();
}
}
}
using System.Web.Mvc;
namespace MvcApplication2.Controllers
{
public class ActionResultsController : Controller
{
//http://localhost/actionresults/welcome
public string Welcome()
{
return "Hello, World";
}
//http://localhost/actionresults/index
public ActionResult Index() // or ContentResult
{
return Content("Hello, World");
}
//http://localhost/actionresults/SendMail
public void SendMail()
{
}
public ActionResult SendMailCompleted() // or EmptyResult
{
// do whatever
return new EmptyResult();
}
public ActionResult GetFile() // or FilePathResult
{
return File(Server.MapPath("~/content/site.css"), "text/css", "mySite.css");
}
public ActionResult UnauthorizedStatus() // or HttpStatusCodeResult/HttpUnauthorizedResult
{
return new HttpUnauthorizedResult("You need to login first.");
}
public ActionResult Status() // or HttpStatusCodeResult
{
return new HttpStatusCodeResult(501, "Server Error");
}
public ActionResult GetJavaScript() // or JavaScriptResult
{
return JavaScript("...JavaScript...");
}
public ActionResult GetJson() // or JsonResult
{
var obj = new { prop1 = 1, prop2 = "data" };
return Json(obj, JsonRequestBehavior.AllowGet);
}
public ActionResult RedirectTo() // or RedirectResult
{
return RedirectPermanent("http://www.site.com");
//return RedirectToAction("Home", "Index");
}
public ActionResult ShowView() // or ViewResult
{
return View();
}
}
}
[TestMethod]
public void TestMethod1()
{
// Arrange
var controller = new ActionResultsController();
// Act
var result = controller.Index() as ContentResult;
// Assert
Assert.NotNull(result);
Assert.AreEqual( "Hello, World", result.Content);
}
var numbers1_CS11 = new[] { 1, 2, 3 };
var numbers1_CS_11 = new int[] { 1, 2, 3 };
int[] numbers1_CS12 = [ 1, 2, 3 ];
error CS9176: There is no target type for the collection expression.
Span<string> span1_CS11 = new string[] { "AC", "AL" };
Span<string> span1_CS12 = [ "AC", "AL" ];
ReadOnlySpan<string> readOnlySpan_CS12 = [ "Africa", "Asia", "Europa"];
int[][] array2D_CS11 = { new int[] { 2002, 2006, 2010}, new int[] { 2014, 2018}, new int[] { 2022, 2026, 2030} };
int[][] array2D_CS12 = [ [2002, 2006, 2010], [2014, 2018], [2022, 2026, 2030] ];
List<string> list_CS11 = new List<string> { "Item 1", "Item 2" };
List<string> list_CS12 = [ "Item 1", "Item 2" ];
// Before C#12 List<User> users = new List<User>(); // or var users = new List<User>(); // or List<User> user = new(); // C#12 List<User> users = [];
int[] numbers1_CS12 = [ 1, 2, 3 ]; int[] numbers2_CS12 = [ 4, 5, 6 ];
int[] allItems = [ ..numbers1_CS12, ..numbers2_CS12 ];
int[] allItems_CS11 = numbers1_CS12.Concat(numbers2_CS12).ToArray();
int[] join = [..a, ..b, ..c, 6, 5];
int[] a =[1, 2, 3]; Span<int> b = [2, 4, 5, 4, 4]; List<int> c = [4, 6, 6, 5]; List<int> join = [..a, ..b, ..c, 6, 5];
List<(string, int)> otherScores = [("Dave", 90), ("Bob", 80)];
(string name, int score)[] scores = [("Alice", 90), ..otherScores, ("Charlie", 70)];
$('#someDiv') .html('There are '+$('a').size()+' link(s) on this page.');
()size
تعداد عناصر موجود در مجموعه را محاسبه میکند
پارامترها
بدون پارامتر
خروجی
تعداد عناصر مجموعه
$('img[alt]')[0]
دستور زیر مانند دستور قبلی عمل میکند:(get(index
برای واکشی یک یا تمام عناصر موجود در مجموعه استفاده میشود. اگر برای این متد پارامتری ارسال نشود، تمام عناصر را در قالب یک آرایه جاوااسکریپت بر میگرداند، اما در صورت ارسال یک پارامتر، تنها آن عنصر را بر میگرداند.
پارامتر
شماره اندیس یک عنصر که میبایست یک مقدار عددی باشد.
خروجی
یک یا آرایه ای از عناصر
$('img[alt]').get(0)
var allLabeledButtons = $('label+button').get();
var n = $('img').index($('img#findMe')[0]);
(index(element
عنصر ارسالی را در مجموعه عناصر پیدا میکند، سپس شماره اندیس ان را بر میگرداند. اگر چنین عنصری در مجموعه یافت نشد خروجی 1- خواهد بود.
پارامتر
پارامتر این متد میتواند یک عنصر و یا یک انتخاب کننده باشد که خروجی انتخاب کننده نیز در نهایت یک عنصر خواهد بود.
خروجی
شماره اندیس عنصر در مجموعه
$('img[alt],img[title]')
$('img[alt]').add('img[title]')
َ(add(expressionاصلاح عناصر یک مجموعه عنصر انتخاب شده
ابتدا یک کپی از مجموعه انتخاب شده ایجاد میکند، سپس با افزودن محتویات پارامتر expression به آن نمونه، یک مجموعه جدید تشکیل میدهد. پارامتر expression میتواند حاوی یک انتخاب کننده، قطعه کد HTML، یک عنصر و یا آرایه ای از عناصر باشد.
پارامتر
در این پارامتر مواردی (مانند رشته، آرایه، المان) که میخواهیم به مجموعه عناصر انتخاب شده اضافه شوند قرار میگیرد. که میتواند انتخاب کننده، قطعه کد HTML، یک عنصر و یا ارایه ای از عناصر باشد.
خروجی
یک کپی از مجموعه اصلی به علاوه موارد اضافه شده.
$('img[title]').not('[title*=puppy]')
(not(expressionاین شیوه برای ایجاد مجموعه هایی که انتخاب کنندهها قادر به ساخت آنها نمیباشند، کاربرد بسیار مناسبی دارد، زیرا از تکنیکهای برنامه نویسی استفاده میکند و دست ما را برای اعمال انتخابهای گوناگون باز میکند.
ابتدا یک کپی از مجموعه انتخاب شده ایجاد میکند، سپس از آن کپی عناصری را که expression مشخص میکند را حذف مینماید.
پارامتر
این پارامتر تعیین کننده عناصر در نظر گرفته شده برای حذف میباشد. این پارامتر میتواند یک عنصر، ارایه ای از عناصر، انتخاب کننده و یا یک تابع باشد.
اگر این پارامتر تابع باشد، تک تک عناصر مجموعه به آن ارسال میشوند و هر یک که خروجی تابع را برابر با مقدار true کند، حذف میشود.
خروجی
یک کپی از مجموعه اصلی بدون موارد حذف شده.
$('td').filter(function(){return this.innerHTML.match(/^\d+$/)})
(filter(expressionایجاد یک زیر مجموعه از مجموعه عناصر انتخاب شده
ابتدا یک کپی از مجموعه انتخاب شده ایجاد میکند، سپس از آن کپی عناصری را که expression مشخص میکند را حذف مینماید.
پارامتر
این پارامتر تعیین کننده عناصر در نظر گرفته شده برای حذف میباشد. این پارامتر میتواند یک عنصر، ارایه ای از عناصر، انتخاب کننده و یا یک تابع باشد.
اگر این پارامتر تابع باشد، تک تک عناصر مجموعه به آن ارسال میشوند و هر یک که خروجی تابع را برابر با مقدار false کند، حذف میشود.
خروجی
یک کپی از مجموعه اصلی بدون عناصر حذف شده.
(slice(begin, endاگر بخواهیم از یک مجموعه کلی، تنها یک عنصر را در قالب یک مجموعه انتخاب کنیم میتوانیم از متد ()slice استفاده کنیم و مکان آن عنصر در مجموعه را به آن ارسال کنیم. دستور زیر مثالی از این حالت میباشد:
ایجاد و برگرداندن یک مجموعه جدید از بخشی از عناصر پشت سر هم در یک مجموعه اصلی.
پارامتر
begin: پارامتر begin که یک پارامتر عددی میباشد و مقدار اولیه آن از صفر آغاز میشود، نشان دهنده اولین عنصری است که میخواهیم در مجموعه جدید حضور داشته باشد.
end: پارامتر دوم که آن هم یک پارامتر عددی میباشد و از صفر آغاز میشود، در این متد اختیاری است. این پارامتر اولین عنصری است که نمیخواهیم از آن به بعد در مجموعه جدید حضور داشته باشد را مشخص میکند. اگر مقداری برای این پارامتر ننویسیم، به صورت پیش فرض تا انتهای مجموعه انتخاب میشود.
خروجی
یک مجموعه عنصر جدید.
$('*').slice(2,3);
$('*').slice(0,4);
$('*').slice(4);
توضیح | متد |
مجموعه ای را برمی گرداند که شامل تمام فرزندان بدون تکرار از عناصر مجموعه میباشد. | () children |
مجموعه ای شامل محتویات تمام عناصر برمی گرداند. (از این متد معمولا برای عناصر iframe استفاده میشود) | () contents |
مجموعه ای شامل فرزندان پدرش که بعد از خود این عنصر میباشند را برمی گرداند. این مجموعه عنصر تکراری ندارد. | () next |
مجموعه ای شامل تمام فرزندان پدرش که بعد از خود این عنصر میباشند را بر میگرداند. | () nextAll |
مجموعه ای شامل نزدیکترین پدر اولین عنصر مجموعه را بر میگرداند. | () parent |
مجموعه ای شامل تمام پدران مستقیم عناصر مجموعه را بر میگرداند. این مجموعه عنصر تکراری ندارد. | () parents |
مجموعه ای شامل فرزندان پدرش که قبل از خود این عنصر میباشند را برمی گرداند. این مجموعه عنصر تکراری ندارد. | () prev |
مجموعه ای شامل تمام فرزندان پدرش که قبل از خود این عنصر میباشند را بر میگرداند. | () prevAll |
مجموعه ای بدون عنصر تکراری را بر میگرداند که شامل تمام فرزندان پدر خود عنصر خواهد بود. | () siblings |
wrappedSet.find('p cite')
$('p cite', wrapperSet)
(find(selectorجهت پیدا کردن عناصری که داخل یک wrapperSet میتوانیم از متد دیگری به نام ()contains نیز استفاده کنیم. این متد مجموعه ای را بر میگرداند که شامل تمام عناصری است که در انتخاب کننده پارامتر ورودی است. مثلا
یک مجموعه عنصر جدید ایجاد میکند که شامل فرزندان عناصر مجموعه قبل میشود.
پارامتر
یک انتخاب کننده است که در قالب یک رشته به این متد ارسال میشود.
خروجی
یک مجموعه عنصر جدید
$('p').contains('Lorem ipsum')
(contains(textآخرین متدی که به بررسی آن میپردازیم متد ()is میباشد. با استفاده از این متد میتوانیم اطمینان حاصل کنیم که دست کم یک عنصر از مجموعه عناصر، شرایط مشخص شده توسط ما را دارا باشد. یک انتخاب کننده به این متد ارسال میشود، اگر عنصری از مجموعه عناصر انتخاب شد، خروجی متد true میشود و در غیر این صورت مقدار false بر گردانده خواهد شد. برای مثال:
مجموعه ای از عناصر که شامل متن ورودی میباشند را بر میگرداند.
پارامتر
رشته ورودی که میخواهیم در عنصر فراخوان متد جستجو شود.
خروجی
مجموعه ای از عناصر از نوع فراخوان متد را بر میگرداند که شامل متن ورودی باشد.
var hasImage = $('*').is('img');
(is(selector
بررسی میکند که آیا عنصری در مجموعه وجود دارد که انتخاب کننده ارسالی آن را انتخاب کند؟
پارامتر
یک انتخاب کننده است که در قالب یک رشته به این متد ارسال میشود.
خروجی
مقدار true در صورت وجود دست کم یک عنصر و false در صورت عدم وجود توسط تابع برگردانده میشود.
$('img').clone().appendTo('#somewhere');
$('img').clone().appendTo('#somewhere').end().addClass('beenCloned');
()endشاید در نظر گرفتن مجموعهها در متدهای زنجیره ای به شکل یک پشته به درک بهتر از متد ()end کمک کند. هر زمان که یک مجموعه جدید در زنجیره ایجاد میشود، آن مجموعه به بالای پشته افزوده میشود، اما با فراخوانی متد ()end، بالاترین مجموعه از این پشته برداشته میشود و مجدادا مجموعه پیشین در زنجیره قرار میگیرد.
در متدهای زنجیره ای استفاده میشود و از مجموعه کنونی یک پشتیبان میگیرد تا همان مجموعه در زنجیره جریان داشته باشد.
پارامتر
ندارد
خروجی
مجموعه عنصر قبلی
()andSelfدر مباحث بعدی کار با صفتها و ویژگیهای عناصر بحث خواهد شد.
دو مجموعه پیشین در یک زنجیره را با یکدیگر ادغام میکند.
پارامتر
ندارد
خروجی
مجموعه عنصری ادغام شده
var func = columnsMap[model.FilterByColumn].Compile();
{Method = <Internal Error evaluating expression>}
public class MyBaseClass(string s); // no body required
class Foo; struct Bar; interface IFoo;
var listOfRows = new List<Rpt>(); foreach (var property in doc.GetType().GetProperties()) { var attr = property.GetCustomAttributes(true).OfType<DisplayNameAttribute>().FirstOrDefault(); if (attr == null) continue; listOfRows.Add(new Rpt { Title = attr.DisplayName, Value = property.GetValue(doc, null).ToSafeString() }); } dataSource.StronglyTypedList(listOfRows);
var mockIdentityVerifier = new Mock<IIdentityVerifier>(MockBehavior.Strict);
Test method Loans.Tests.LoanApplicationProcessorShould.Accept threw exception: Moq.MockException: IIdentityVerifier.Initialize() invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup.
mockIdentityVerifier.Setup(x => x.Initialize());
try { _creditScorer.CalculateScore(application.Applicant.Name, application.Applicant.Address); } catch { return application.IsAccepted; }
mockCreditScorer.Setup(x => x.CalculateScore(It.IsAny<string>(), It.IsAny<string>())) .Throws(new InvalidOperationException("Test Exception"));
Assert.IsFalse(application.IsAccepted);
using System; namespace Loans.Models { public class CreditScoreResultArgs : EventArgs { public int Score { get; set; } } }
public interface ICreditScorer { event EventHandler<CreditScoreResultArgs> ResultAvailable;
mockCreditScorer.Raise(x => x.ResultAvailable += null, new CreditScoreResultArgs());
mockCreditScorer.Setup(x => x.CalculateScore(It.IsAny<string>(), It.IsAny<string>())) .Raises(x => x.ResultAvailable += null, new CreditScoreResultArgs());
namespace Loans.Tests { [TestClass] public class LoanApplicationProcessorShould { [TestMethod] public void AcceptUsingPartialMock() { var product = new LoanProduct {Id = 99, ProductName = "Loan", InterestRate = 5.25m}; var amount = new LoanAmount {CurrencyCode = "Rial", Principal = 2_000_000_0}; var applicant = new Applicant {Id = 1, Name = "User 1", Age = 25, Address = "This place", Salary = 1_500_000_0}; var application = new LoanApplication {Id = 42, Product = product, Amount = amount, Applicant = applicant}; var mockIdentityVerifier = new Mock<IdentityVerifierServiceGateway>(); mockIdentityVerifier.Setup(x => x.CallService(applicant.Name, applicant.Age, applicant.Address)) .Returns(true); var mockCreditScorer = new Mock<ICreditScorer>(); mockCreditScorer.Setup(x => x.ScoreResult.ScoreValue.Score).Returns(110_000); var sut = new LoanApplicationProcessor(mockIdentityVerifier.Object, mockCreditScorer.Object); sut.Process(application); Assert.IsTrue(application.IsAccepted); } } }
public virtual bool CallService(string applicantName, int applicantAge, string applicantAddress)
public bool Validate(string applicantName, int applicantAge, string applicantAddress) { Connect(); var isValidIdentity = CallService(applicantName, applicantAge, applicantAddress); LastCheckTime = DateTime.Now; Disconnect(); return isValidIdentity; }
public bool Validate(string applicantName, int applicantAge, string applicantAddress) { Connect(); var isValidIdentity = CallService(applicantName, applicantAge, applicantAddress); LastCheckTime = GetCurrentTime(); Disconnect(); return isValidIdentity; } public virtual DateTime GetCurrentTime() { return DateTime.Now; }
var expectedTime = new DateTime(2000, 1, 1); mockIdentityVerifier.Setup(x => x.GetCurrentTime()) .Returns(expectedTime); // ... Assert.AreEqual(expectedTime, mockIdentityVerifier.Object.LastCheckTime);
mockIdentityVerifier.Protected().Setup<bool>( "CallService",applicant.Name, applicant.Age, applicant.Address) .Returns(true); var expectedTime = new DateTime(2000, 1, 1); mockIdentityVerifier.Protected().Setup<DateTime>("GetCurrentTime") .Returns(expectedTime);
interface IIdentityVerifierServiceGatewayProtectedMembers { DateTime GetCurrentTime(); bool CallService(string applicantName, int applicantAge, string applicantAddress); }
mockIdentityVerifier.Protected() .As<IIdentityVerifierServiceGatewayProtectedMembers>() .Setup(x => x.CallService(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<string>())) .Returns(true); var expectedTime = new DateTime(2000, 1, 1); mockIdentityVerifier.Protected() .As<IIdentityVerifierServiceGatewayProtectedMembers>() .Setup(x => x.GetCurrentTime()) .Returns(expectedTime);
using System; namespace Loans.Services.Contracts { public interface INowProvider { DateTime GetNow(); } }
public class IdentityVerifierServiceGateway : IIdentityVerifier { private readonly INowProvider _nowProvider; public DateTime LastCheckTime { get; private set; } public IdentityVerifierServiceGateway(INowProvider nowProvider) { _nowProvider = nowProvider; }
public bool Validate(string applicantName, int applicantAge, string applicantAddress) { Connect(); var isValidIdentity = CallService(applicantName, applicantAge, applicantAddress); LastCheckTime = _nowProvider.GetNow(); // ...
var mockNowProvider = new Mock<INowProvider>(); mockNowProvider.Setup(x => x.GetNow()).Returns(expectedTime); var mockIdentityVerifier = new Mock<IdentityVerifierServiceGateway>(mockNowProvider.Object);