Pet Store Fullstack project with React18, ASP.NET Core7 WebAPI, TypeScript and Entity Framework Core
In this video, we will create a Fullstack Pet Store project using React18, ASP.NET Core WebAPI (.NET 7),TypeScript and Entity Framework Core. The focus of this project is to show you how you can build a Fullstack project from 0 to 100 and build a simple CRUD application with ASP.NET Core WebAPI .
Asp.Net Core WebSockets Vs SignalR. Which should you use? (Full Course)
In this video we build 2 separate chat applications, one using Asp.Net Core WebSockets and the other using SignalR, allowing you to compare approaches and decide on which one works best for you. In both cases we build them with C#, .NET Core and JavaScript. You’ll also learn about:
- .NET Core Request Pipeline
- Request Delegates
- Asynchronous Programming in .NET (Async / Await)
- Introduction to Dependency Injection
اشتراکها
دوره مقدماتی React 18
var dateTime = PersianDateTime.Parse(date, $"{hours.ToString("00")}:{minutes.ToString("00")}:{seconds.ToString("00")}").ToDateTime();
اشتراکها
دوره 14 ساعته React 18
یک نکتهی تکمیلی: روش تنظیم TimeSpan در فایلهای config
قالب ذکر TimeSpan در فایلهای config به صورت d.hh:mm:ss.ff است (d در اینجا، تعداد روز است و ff، کسری از ثانیه)؛ مانند:
"WaitForExit": "0.00:02:00.0000"
یک مثال دیگر:
"1.02:03:04" is 1 day, 2 hours, 3 mins, 4 seconds
Window Functionها برای اولین بار در نسخه SQL Server 2005 ارائه گردیدند، و در ورژنهای جدیدتر SQL Server، به تعداد این فانکشنها افزوده شده است.
تعریف Window Function :
معمولا از این نوع فانکشنها روی مجموعه ای از ROWهای یک جدول، در جهت اعمال عملیاتهای محاسباتی ،ارزیابی داده ها، رتبه بندی و غیرو... استفاده میگردد، به بیان سادهتر بوسیله Window Functionها میتوان، ROWهای یک جدول را گروه بندی نمود. و روی گروهها از توابع جمعی (Aggregate Functions ) استفاده کرد. این نوع فانکشنها از قابلیت و انعطاف پذیری زیادی برخوردار میباشند، و بوسیله آنها میتوان نتایج (خروجی) بسیار مفیدی از Query ها، بدست آورد، معمولا از این نوع فانکشنها در Data Mining (داده کاوی) و گزارشگیریها استفاده میگردد. و آگاهی و روش استفاده از Window Functionها برای برنامه نویسان و DBA ها، میتواند بسیار مفید باشد.
مفهوم Window Function مطابق استاندارد ISO و ANSI میباشد، و دیتابیس هایی همچون Oracle،DB2،Sybase از آن پشتیبانی مینمایند.برای اطلاعات بیشتر میتوانید به سایتهای زیر مراجعه کنید:
کلمه "Window" در Window Function، به مجموعه ROW هایی اشاره میکند، که محاسبات و ارزیابی و غیرو... روی آنها اعمال میگردد.
Window Functionها برای ارائه قابلیتهای خود، از Over Clause استفاده میکنند. اگر مقاله آشنایی با Row_Number،Rank،Dense_Rank،NTILE را مطالعه کرده باشید، میتوان هریک از آنها را یک Window Function دانست.
برای شروع، به بررسی Over Clause میپردازیم، و Syntax آن به شرح ذیل میباشد:
OVER ( [ <PARTITION BY clause> ] [ <ORDER BY clause> ] [ <ROW or RANGE clause> ] ) <PARTITION BY clause> ::= PARTITION BY value_expression , ... [ n ] <ORDER BY clause> ::= ORDER BY order_by_expression [ COLLATE collation_name ] [ ASC | DESC ] [ ,...n ] <ROW or RANGE clause> ::= { ROWS | RANGE } <window frame extent> <window frame extent> ::= { <window frame preceding> | <window frame between> } <window frame between> ::= BETWEEN <window frame bound> AND <window frame bound> <window frame bound> ::= { <window frame preceding> | <window frame following> } <window frame preceding> ::= { UNBOUNDED PRECEDING | <unsigned_value_specification> PRECEDING | CURRENT ROW } <window frame following> ::= { UNBOUNDED FOLLOWING | <unsigned_value_specification> FOLLOWING | CURRENT ROW } <unsigned value specification> ::= { <unsigned integer literal> }
OVER دارای سه آرگومان اختیاری است که هر کدام را به تفصیل بررسی میکنیم:
1- PARTITION BY clause : بوسیله این پارامتر میتوانیم Rowهای یک جدول را گروه بندی نماییم. این پارامتر یک value_expression می پذیرد. یک Value_expression میتواند نام یک ستون ، یک Scalar Subquery ، Scalar Function و غیرو باشد.
2- ORDER BY clause : از نامش مشخص است و برای Sort استفاده میشود، و ویژگیهای Order By در آن اعمال میگردد. به جز Offset.
3- ROW or RANGE clause :این پارامتر بیشتر برای محدود نمودن Row در یک Partition (گروه) مورد استفاده قرار میگیرد، به عنوان مثال نقطه شروع و پایان را میتوان بوسیله پارامتر فوق تعیین نمود.
Row و Range نسبت به هم یک تفاوت عمده دارند،و آن این است که، اگر از ROW Clause استفاده نمایید، ارتباط ROWهای قبلی یا بعدی، نسبت به Row جاری،بصورت فیزیکی (physical association ) سنجیده میشود، بطوریکه با استفاده از Range Clause ارتباط سطرهای قبلی و بعدی، نسبت به سطر جاری بصورت منطقی (logical association ) در نظر گرفته میشود. ممکن است درک این مطلب کمی سخت باشد، در ادامه با مثالهایی که بررسی مینماییم، براحتی تفاوت این دو را متوجه میشوید.
Row یا Range در قالبهای متفاوتی مقدار میپذیرند، که هر کدام را بررسی میکنیم:
UNBOUNDED PRECEDING : بیانگر اولین سطر Partition میباشد. UNBOUNDED PRECEDING فقط نقطه شروع را مشخص مینماید.
UNBOUNDED FOLLOWING : بیانگر آخرین سطر Partition میباشد. UNBOUNDED FOLLOWING فقط نقطه پایانی را مشخص مینماید.
CURRENT ROW : اولین سطر جاری یا آخرین سطر جاری را مشخص مینماید.
n PRECEDING یا unsigned value specification> PRECEDING> : تعداد سطرهای قبل از سطر جاری را تعیین میکند، n یا <unsigned value specification>تعداد سطرهای قبل از سطر جاری را تعیین مینماید. از n PRECEDING نمی توان برای Range استفاده نمود.
n FOLLOWING یا unsigned value specification> FOLLOWING> : تعداد سطرهای بعد از سطر جاری را تعیین میکند، n یا<unsigned value specification> تعداد سطر های بعد از سطر جاری را تعیین مینماید. از n FOLLOWING نمی توان برای Range استفاده نمود.
<BETWEEN <window frame bound > AND <window frame bound : از چارچوب فوق برای Range و Row میتوان استفاده نمود، و نقطه آغازین و نقطه پایانی توسط قالب فوق تعیین میگردد. نکته قابل توجه آن است که نقطه پایانی نمیتواند، کوچکتر از نقطه آغازین گردد.
در ادامه برای درک هرچه بیشتر تعاریف بیان شده، چندین مثال میزنیم و هر کدام را بررسی مینماییم:
در ابتدا Script زیر را اجرا نمایید، که شامل جدولی به نام Revenue (سود،درآمد) و درج چند درکورد در آن:
CREATE TABLE REVENUE ( [DepartmentID] int, [Revenue] int, [Year] int ); insert into REVENUE values (1,10030,1998),(2,20000,1998),(3,40000,1998), (1,20000,1999),(2,60000,1999),(3,50000,1999), (1,40000,2000),(2,40000,2000),(3,60000,2000), (1,30000,2001),(2,30000,2001),(3,70000,2001)
مثال اول : میخواهیم براساس فیلد DepartmentID جدول Revenue را Partition بندی نماییم و از توابع جمعی AVG و SUM روی فیلد درآمد(Revenue) استفاده کنیم.
ابتدا Script زیر را اجرا میکنیم:
select *, avg(Revenue) OVER (PARTITION by DepartmentID) as AverageRevenue, sum(Revenue) OVER (PARTITION by DepartmentID) as TotalRevenue from REVENUE order by departmentID, year;
خروجی بصورت زیر خواهد بود:
مطابق شکل، جدول براساس فیلد DepartmentID به سه Partition تقسیم شده است، و عملیات میانگین و جمع روی فیلد Revenue انجام شده است و عملیات Sort روی هرگروه بطور مستقل انجام گرفته است. چنین کاری را نمیتوانستیم بوسیله Group By انجام دهیم.
مثال دوم : نحوه استفاده از ROWS PRECEDING،در این مثال قصد داریم عملیات جمع را روی فیلدRevenue انجام دهیم. بطوریکه جمع هر مقدار برابر است با سه مقدار قبلی + مقدار جاری:
لطفا رکوردهای زیر را به جدول فوق درج نمایید:
insert into REVENUE values(1,90000,2002),(2,20000,2002),(3,80000,2002), (1,10300,2003),(2,1000,2003), (3,90000,2003), (1,10000,2004),(2,10000,2004),(3,10000,2004), (1,20000,2005),(2,20000,2005),(3,20000,2005), (1,40000,2006),(2,30000,2006),(3,30000,2006), (1,70000,2007),(2,40000,2007),(3,40000,2007), (1,50000,2008),(2,50000,2008),(3,50000,2008), (1,20000,2009),(2,60000,2009),(3,60000,2009), (1,30000,2010),(2,70000,2010),(3,70000,2010), (1,80000,2011),(2,80000,2011),(3,80000,2011), (1,10000,2012),(2,90000,2012),(3,90000,2012)
سپس Script زیر را اجرا مینماییم:
select Year, DepartmentID, Revenue, sum(Revenue) OVER (PARTITION by DepartmentID ORDER BY [YEAR] ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) as Prev3 From REVENUE order by departmentID, year;
خروجی :
در Script بالا، جدول را براساس فیلد DepartmentID گروه بندی میکنیم، که سه گروه ایجاد میشود، هر گروه را بطور مستقل، روی فیلد Year بصورت صعودی مرتب مینماییم. حال برای آنکه بتوانیم سیاست جمع، روی فیلد Revenue، را پیاده سازی نماییم ، قطعه کد زیر را در Script بالا اضافه کردیم.
ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) as Prev3
برای شرح چگونگی استفاده از PRECEDING،فقط به شرح گروه اول بسنده میکنیم. مقدار جمع فیلد Revenue سطر اول، که قبل از آن سطری وجود ندارد، برابر است با مقدار خود، یعنی 10030، مقدار جمع فیلد Revenue سطر دوم برابر است با حاصل جمع مقدار فیلدRevenue سطر اول و دوم ، یعنی 30030 . این روند تا سطر چهار ادامه دارد، اما برای بدست آوردن مقدار جمع فیلدRevenue سطر پنجم، مقدار جمع فیلد Revenue سطر دوم،سوم،چهارم و پنجم در نظر گرفته میشود، و مقدار فیلدRevenue سطر اول در حاصل جمع در نظر گرفته نمیشود،بنابراین مقدار جمع فیلد Revenue سطر پنجم برابر است با 180000. در صورت مسئله گفته بودیم، مقدار جمع فیلد Revenue هر سطر جاری برابر است با حاصل جمع مقدارسطر جاری و مقادیر سه سطر ماقبل خود.
مثال سوم: نحوه استفاده از ROWS FOLLOWING، این مثال عکس مثال دوم است، یعنی حاصل جمع مقدار فیلد Revenue هر سطر برابر است با حاصل جمع سطر جاری با سه سطر بعد از خود. بنابراین Script زیر را اجرا نمایید:
select Year, DepartmentID, Revenue, sum(Revenue) OVER (PARTITION by DepartmentID ORDER BY [YEAR] ROWS BETWEEN CURRENT ROW AND 3 FOLLOWING) as Next3 From REVENUE order by departmentID, year;
خروجی :
مطابق شکل مقدار جمع فیلد اول برابراست با حاصل جمع مقدار سطر جاری و سه سطر بعد از آن.
نکته ای که در مثالهای دوم و سوم،می بایست به آن توجه نمود، این است که در زمان استفاده از Row یا Range ، استفاده از Order by در Partition الزامی است، در غیر این صورت با خطا مواجه میشوید.
نحوه استفاده از UNBOUNDED PRECEDING ، این امکان در T-SQL Server 2012 افزوده شده است.
مثال چهار: در این مثال میخواهیم کمترین سود بدست آمده در چند سال را بدست آوریم:
ابتدا Script زیر را اجرا نمایید:
select Year, DepartmentID, Revenue, min(Revenue) OVER (PARTITION by DepartmentID ORDER BY [YEAR] ROWS UNBOUNDED PRECEDING) as MinRevenueToDate From REVENUE order by departmentID, year;
خروجی:
طبق تعریف UNBOUNDED PRECEDING اولین سطر هر Partition را مشخص مینماید، و چون از PRECEDING استفاده کرده ایم، بنابراین مقایسه همیشه بین سطر جاری و سطرهای قبل از آن انجام میپذیرد. بنابراین خواهیم داشت، کمترین مقدار فیلد Revenue در سطر اول، برابر با مقدار خود میباشد، چون هیچ سطری ماقبل از آن وجود ندارد. در سطر دوم مقایسه کمترین مقدار، بین 20000 و 10030 انجام میگیرد، که برابر است با 10030، در سطر سوم، مقایسه بین مقادیر سطر اول،دوم و سطر سوم صورت میگیرد، یعنی کمترین مقدار بین 40000،20000 و 10030، بنابراین کمترین مقدار سطر سوم برابر است با 10030.
به بیان سادهتر برای بدست آوردن کمترین مقدار هر سطر، مقدار سطر جاری با مقادیر همه سطرهای ماقبل خود مقایسه میگردد.
برای بدست آوردن کمترین مقدار در سطر ششم، مقایسه بین مقادیر سطرهای اول،دوم،سوم،چهارم،پنجم و ششم صورت میگیرد که عدد 10000 بدست میآید و الی آخر...
نکنه: اگر در Over Clause شرط Order by را اعمال نماییم، اما از Row یا Range استفاده نکنیم، SQL Server بصورت پیش فرض از قالب زیر استفاده مینماید:
RANGE UNBOUNDED PRECEDING AND CURRENT ROW
برای روشنتر شدن مطلب فوق مثالی میزنیم:
ابتدا Script زیر را اجرا نمایید، که شامل ایجاد یک جدول و درج چند رکورد در آن میباشد:
CREATE TABLE Employees ( EmployeeId INT IDENTITY PRIMARY KEY, Name VARCHAR(50), HireDate DATE NOT NULL, Salary INT NOT NULL ) GO INSERT INTO Employees (Name, HireDate, Salary) VALUES ('Alice', '2011-01-01', 20000), ('Brent', '2011-01-15', 19000), ('Carlos', '2011-02-01', 22000), ('Donna', '2011-03-01', 25000), ('Evan', '2011-04-01', 18500) GO
سپس Script زیر را اجرا نمایید:
SELECT Name, Salary, AVG(Salary) OVER(ORDER BY HireDate) AS avgSalary FROM Employees GO
خروجی :
حال اگر Script زیر را نیز اجرا نمایید، خروجی آن مطابق شکل بالا خواهد بود:
SELECT Name, Salary, AVG(Salary) OVER(ORDER BY HireDate RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS avgSalary FROM Employees GO
توضیح درباره Script بالا، در این روش برای بدست آوردن میانگین هر سطر، مقدار سطر جاری با مقادیر سطرهای ماقبل خود جمع و تقسیم بر تعداد سطر میشود.
سطر دوم 20000 + 19000 تقسیم بر دو برابر است با 19500
میانگین سطر پنجم، حاصل جمع فیلد Salary همه مقادیر سطرها تقسیم بر 5
*** اگر بخواهید بوسیله Over Clause ، میانگین همه سطرها یکسان باشد میتوانید از Script زیر استفاده نمایید:
SELECT Name, Salary, AVG(Salary) OVER(ORDER BY HireDate RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS avgSalary FROM Employees GO
خروجی :
منظور از ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING یعنی در محاسبه میانگین برای هر سطر تمامی مقادیر سطرهای دیگر در نظر گرفته شود.
پایان بخش اول
امیدوارم مفید واقع شده باشد.
Supports Gregorian and Jalali calender
Uses context api to share data
Supports three types of calender: single day - date range - multiple dates
Fully customizable
Supports maximum and minimum dates
Capability to add a list of disabled dates
Supports time for single and range type
Capability to mark weekends
Function called for change, open and close events
Supports Typescript
Uses context api to share data
Supports three types of calender: single day - date range - multiple dates
Fully customizable
Supports maximum and minimum dates
Capability to add a list of disabled dates
Supports time for single and range type
Capability to mark weekends
Function called for change, open and close events
Supports Typescript
در مطلب قبل متوجه شدیم که Enumerable و Enumerator چه چیزی هستند و آنها را چگونه میسازند. در انتهای آن مطلب نیز قطعه کدی وجود داشت که در آن دیدیم چگونه یک شئ Enumerable میتواند در عملیاتی نسبتاً پیچیده یک شئ Enumerator ایجاد کند.
حال میخواهیم قابلیت زبانیای را بررسی کنیم که در اصل مشابه همین کاری که ما انجام دادیم یعنی ایجاد شئ جداگانهٔ Enumerator و برگرداندن یک نمونه از آن در زمانی که ما GetEnumerator را از Enumerableمان فراخوانی میکنیم را انجام میدهد.
yield و نحوهٔ پیادهسازی آن
در اینجا قطعه کدی قرار دارد که در اصل جایگزین دو کلاسیاست که در انتهای مطلب قبل قرار داشت که به کمک قابلیت yield آن را بازنویسی کردهایم:
public class ArrayEnumerable<T> : IEnumerable<T> { T[] _array; public ArrayEnumerable(T[] array) { _array = array; } public IEnumerator<T> GetEnumerator() { int index = 0; while (index < _array.Length) { yield return _array[index]; index++; } yield break; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } }
(yield break در اینجا مانند return در یک تابع/متد با نوع خروجی void اضافیاست و فقط برای آشنایی با syntax دومی که yield در سیشارپ پشتیبانی میکند قرار داده شدهاست)
همانطور که میبینیم کد قبلی ما به مقدار بسیاری سادهتر و خواناتر شد و برای فهم آن کافی است که مفهوم yield را بدانیم.
yield به معنای برآوردن یا ارائهکردن کلید واژهای است که میتوان آن را اینگونه تصور کرد که با هر با صدا زدهشدن کد را متوقف میکند و نتیجهای را برمیگرداند و با درخواست ما برای ادامهٔ کار (با MoveNext) کار خود را از همان جای متوقف شده ادامه میدهد.
حالا اگر کمی دقیقتر باشیم سوالی که باید برای ما پیش بیاید این است که آیا CLR خود yield را پشیبانی میکند؟
همانطور که میبینیم کد قبلی ما به مقدار بسیاری سادهتر و خواناتر شد و برای فهم آن کافی است که مفهوم yield را بدانیم.
yield به معنای برآوردن یا ارائهکردن کلید واژهای است که میتوان آن را اینگونه تصور کرد که با هر با صدا زدهشدن کد را متوقف میکند و نتیجهای را برمیگرداند و با درخواست ما برای ادامهٔ کار (با MoveNext) کار خود را از همان جای متوقف شده ادامه میدهد.
حالا اگر کمی دقیقتر باشیم سوالی که باید برای ما پیش بیاید این است که آیا CLR خود yield را پشیبانی میکند؟
این قطعه کدی است که با کمک بازگردانی مجدد همین کلاس به زبان سیشارپ دیده میشود:
public class ArrayEnumerable<T> : IEnumerable<T>, IEnumerable { // Fields private T[] _array; // Methods public ArrayEnumerable(T[] array) { this._array = array; } public IEnumerator<T> GetEnumerator() { return new <GetEnumerator>d__0(0); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } // Nested Types [CompilerGenerated] private sealed class <GetEnumerator>d__0 : IEnumerator<T>, IEnumerator, IDisposable { // Fields private int <>1__state; private T <>2__current; public ArrayEnumerable<T> <>4__this; public int <index>5__1; // Methods [DebuggerHidden] public <GetEnumerator>d__0(int <>1__state) { this.<>1__state = <>1__state; } private bool MoveNext() { switch (this.<>1__state) { case 0: this.<>1__state = -1; this.<index>5__1 = 0; while (this.<index>5__1 < ArrayEnumerable<T>._array.Length) { this.<>2__current = ArrayEnumerable<T>._array[this.<index>5__1]; this.<>1__state = 1; return true; Label_0050: this.<>1__state = -1; this.<index>5__1++; } break; case 1: goto Label_0050; } return false; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } void IDisposable.Dispose() { } // Properties T IEnumerator<T>.Current { [DebuggerHidden] get { return this.<>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return this.<>2__current; } } } }
(توجه: برای خواندن این کد، <...>ها را نادیده بگیرید، اینها هیچ وظیفهٔ خاصی ندارند و کار خاصی نمیکنند)
این کد را که البته چندان خوانا نیست اگر با کد انتهای مطلب قبل مقایسه کنید متوجه میشوید که دارای اشتراکهاییاست. در آن مثال نیز شئ Enumerable یک شئ جداگانه بود (در اینجا یک کلاس درونی است) که هنگامی که GetEnumerator را صدا میزدیم نمونهای از آن ایجاد میشد و بازگردانیده میشد.
در این کد کامپایلر وضعیتهای مختلفی که برای توقف و ادامهٔ کار MoveNext که مهمترین بخش کد هست را با کمک ترکیبی از switch case و goto پیادهسازی کردهاست که با کمی دقت میتوانید متوجه منطق آن شوید :)
ممکن است به نظرتان برسد که این قطعه کد از نظر (حداقل نامگذاری) در سیشارپ صحیح نیست. اینگونه نامگذاریها که از نظر CLR (و زبان IL) درست ولی از نظر زبان سطح بالا نادرست هستند باعث میشوند که از هرگونه برخورد نامی احتمالی با نامهای معتبر تعریف شده توسط کاربر جلوگیری شود.
احتمالاً اگر پیشزمینه نسبت به این مطلب داشته باشید با خود خواهید گفت که «این که واضح بود، اصلاً وظیفهٔ ماشین در سطح پایین نیست که چنین عملی را پشتیبانی کند». واضحبودن این موضوع برای شما شاید به این دلیل باشد که پیادهسازی yield را قبلاً جای دیگری ندیدهاید. برای درک این مطلب در اینجا نحوهٔ پیادهسازی yield را در پایتون بررسی میکنیم.
در این کد کامپایلر وضعیتهای مختلفی که برای توقف و ادامهٔ کار MoveNext که مهمترین بخش کد هست را با کمک ترکیبی از switch case و goto پیادهسازی کردهاست که با کمی دقت میتوانید متوجه منطق آن شوید :)
ممکن است به نظرتان برسد که این قطعه کد از نظر (حداقل نامگذاری) در سیشارپ صحیح نیست. اینگونه نامگذاریها که از نظر CLR (و زبان IL) درست ولی از نظر زبان سطح بالا نادرست هستند باعث میشوند که از هرگونه برخورد نامی احتمالی با نامهای معتبر تعریف شده توسط کاربر جلوگیری شود.
احتمالاً اگر پیشزمینه نسبت به این مطلب داشته باشید با خود خواهید گفت که «این که واضح بود، اصلاً وظیفهٔ ماشین در سطح پایین نیست که چنین عملی را پشتیبانی کند». واضحبودن این موضوع برای شما شاید به این دلیل باشد که پیادهسازی yield را قبلاً جای دیگری ندیدهاید. برای درک این مطلب در اینجا نحوهٔ پیادهسازی yield را در پایتون بررسی میکنیم.
def array_iterator(array): length = len(array) index = 0 while index < length: yield array[index] index = index + 1
اگر کد مفسر پایتون را برای این generator بررسی کنیم متوجه میشویم که پایتون دارای عملگر خاصی در سطح ماشین برای yield است:
همانطور که میبینیم پایتون دارای عملگر خاصی برای پیادهسازی yield بوده و به مانند سیشارپ از قابلیتهای قبلی ماشین برای پیادهسازی yield استفاده نکردهاست.
>>> import dis >>> dis.dis(array_iterator) 2 0 LOAD_GLOBAL 0 (len) 3 LOAD_FAST 0 (array) 6 CALL_FUNCTION 1 9 STORE_FAST 1 (length) 3 12 LOAD_CONST 1 (0) 15 STORE_FAST 2 (index) 4 18 SETUP_LOOP 35 (to 56) >> 21 LOAD_FAST 2 (index) 24 LOAD_FAST 1 (length) 27 COMPARE_OP 0 (<) 30 POP_JUMP_IF_FALSE 55 5 33 LOAD_FAST 0 (array) 36 LOAD_FAST 2 (index) 39 BINARY_SUBSCR 40 YIELD_VALUE 41 POP_TOP 6 42 LOAD_FAST 2 (index) 45 LOAD_CONST 2 (1) 48 BINARY_ADD 49 STORE_FAST 2 (index) 52 JUMP_ABSOLUTE 21 >> 55 POP_BLOCK >> 56 LOAD_CONST 0 (None) 59 RETURN_VALUE
yield و iteratorها قابلیتهای زیادی را در اختیار برنامهنویسان قرار میدهند. برنامهنویسی async یکی از این قابلیتهاست. پیوندهای ابتدای مقالهٔ اول را در این زمینه مطالعه کنید (البته با ورود داتنت ۴.۵ شیوهٔ دیگری نیز برای برنامهنویسی async ایجاد شده). از قابلیتهای دیگر طراحی سادهٔ یک ماشین حالت است.
کد زیر سادهترین حالت یک ماشین حالت را نمایش میدهد که به کمک قابلیت yield سادهتر پیادهسازی شدهاست:
public class SimpleStateMachine : IEnumerable<bool> { public IEnumerator<bool> GetEnumerator() { while (true) { yield return true; yield return false; } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } }
(البته استفاده اینگونه از yield (در حلقهٔ بینهایت) خطرناک است و ممکن است برنامهتان را در اثر بیدقتی قفل کنید، حداقل به همین دلیل بهتر است همیشه چنین اشیائی دارای محدودیت باشند.)
میتوانید از SimpleStateMachine به این شکل استفاده کنید:
new SimpleStateMachine().Take(20).ToList().ForEach(x => Console.WriteLine(x));
که ۲۰ حالت از این ماشین حالت را چاپ خواهد کرد که البته اگر Take را قرار نمیدادیم برنامه را قفل میکرد.