نحوه راه اندازی گوشی های هوشمند و رایانه های شخصی. پرتال اطلاعاتی
  • خانه
  • خطاها
  • ریخته گری نوع ضمنی در سی شارپ. تبدیل خودکار نوع

ریخته گری نوع ضمنی در سی شارپ. تبدیل خودکار نوع

تبدیل نوع داده ضمنی توسط کامپایلر ++C انجام می شود، در حالی که تبدیل داده صریح توسط خود برنامه نویس انجام می شود. در مورد تبدیل نوع داده موارد زیر را خواهم گفت: "نتیجه هر محاسبه به دقیق ترین نوع داده از انواع داده هایی که در محاسبه دخیل هستند تبدیل می شود." برای مثال روشنمن یک جدول با تبدیل نوع داده ارائه خواهم کرد. در جدول عملیات تقسیم را در نظر خواهیم گرفت. بیایید به عنوان یک نوع داده عدد صحیح در نظر بگیریم بین المللی، خوب نوع واقعیداده خواهیم داشت شناور.

جدول 1 - تبدیل نوع داده صریح و ضمنی در C++
ایکس y نتیجه تقسیم مثال
سود سهام تقسیم کننده خصوصی x = 15 y = 2
بین المللی بین المللی بین المللی 15/2=7
بین المللی شناور شناور 15/2=7.5
شناور بین المللی شناور 15/2=7.5

جدول نشان می دهد که با تغییر متغیرها در مکان های مختلف، نتیجه یکسان می ماند (در مورد ما تقسیم کننده و مقسوم علیه است). همه چیز در مورد تبدیل نوع داده ضمنی در مورد تبدیل صریح، برای انجام برخی دستکاری ها، در نتیجه تغییر نتیجه محاسبه ضروری است. ساده ترین راه برای تبدیل صریح انواع داده ها، به عنوان مثال: فرض کنید باید اعداد 15 و 2 را تقسیم کنیم، تقسیم کنیم! 15/2=7. نتیجه مانند جدول است. اما اگر تبدیل های جزئی انجام دهید، به عنوان مثال: 15.0/2=7.5 با این تقسیم عدد 15 واقعی است، یعنی نتیجه واقعی خواهد بود. خود عدد 15 از نظر ریاضی تغییری نکرده است، زیرا 15 = 15.0 است. همین تکنیک را می‌توان برای دو به کار برد، نتیجه یکسان بود، یا می‌توان آن را همزمان روی دو عدد اعمال کرد، اما چرا، اگر یکی از این دو کافی است.

روش دیگر برای تبدیل صریح انواع داده ها این است:

Float(15) / 2 // نتیجه 7.5 است، عدد 15 به نوع داده شناور تبدیل می شود. double(15) / 2 // نتیجه 7.5 است - همان چیزی!!!

C++ نیز فراهم می کند عملیات یکنواختنوع بازیگران:

Static_cast(/*متغیر یا عدد*/)

مثال: static_cast (15)/2 نتیجه 7.5 است
مثال با یک متغیر:

Int ret=15; static_cast (ret)/2 //نتیجه 7.5 است

در مورد یک متغیر، باید بدانید که در خط 2 متغیر ret به نوع داده float تبدیل نمی شود، بلکه فقط یک کپی موقت از متغیر ret با نوع داده float ایجاد می شود. بیایید در عمل همه روشهای صریح و صریح را در نظر بگیریم تبدیل ضمنیانواع داده ها

// pryeobrazovanie.cpp: نقطه ورودی برنامه کنسول را تعریف می کند. #include "stdafx.h" #include #عبارتند از با استفاده از namespace std. int _tmain(int argc, _TCHAR* argv) ( int int_value15 = 15, int_value2 = 2; // دو را اعلام کنید متغیرها را تایپ کنید int float float_value15 = 15، float_value2 = 2; // دو متغیر را اعلام کنید نوع شناورکوت<< fixed << setprecision(2) // определяем, при выводе чисел с плавающей точкой, два знака после запятой << "15 / 2 = " << int_value15 / int_value2 << endl //неявное преобразование типов данных << "15 / 2 = " << int_value15 / float_value2 << endl //неявное преобразование типов данных << "15 / 2 = " << float_value15 / int_value2 << endl //неявное преобразование типов данных << "15 / 2 = " << float_value15 / float_value2 << endl; //неявное преобразование типов данных cout << "15.0 / 2 = " << 15.0 / 2 << endl // явное преобразование типа данных, число 15.0 - число с плавающей точкой << "15 / 2.0 = " << 15 / 2.0 << endl; // явное преобразование типа данных, число 2.0 - число с плавающей точкой cout << "float(int_value15) / int_value2 = " << float(int_value15) / int_value2 << endl // явное преобразование типа данных << "15 / double(2) = " << 15 / double(2) << endl; // используя приводимый тип как функцию cout << "static_cast(15) / 2 = " << static_cast(15) / 2 << endl // унарная операция приведения типа << "static_cast(15) = " << static_cast(15) << endl // можно печатать различные символы из таблицы ASCII, << "static_cast(20) = " << static_cast(20) << endl; // в скобочках прописываем код символа, который находим в таблице ASCII system("pause"); return 0; }

که در خط 5متصل ، این کتابخانه برای استفاده از دستکاری های مختلف مورد نیاز است، در مورد ما - setprecision() ثابت. که در خط 10به طور خاص دو متغیر از نوع ایجاد شده استبین المللی ، به طور مشابه دو متغیر از نوع ایجاد کردشناور در خط 11، این متغیرها برای تبدیل مقادیر آنها به انواع داده های دیگر مورد نیاز خواهند بود. که درخط 12بعد از اپراتورکوت و عملیات را به جریان خروجی تغییر دهید << دو دستکاری کننده وجود دارد fixed و setprecision() . مانیپولاتور ثابت شد - این یک دستکاری پارامتری نیست، زیرا هیچ پارامتری را نمی پذیرد و بدون پرانتز نوشته می شود. این دستکاری کننده همراه با یک دستکاری پارامتری استفاده می شود setprecision() و نمایش ثابت اعشار را انجام می دهد. یک دستکاری کننده setprecision() تعداد ارقام اعشاری و رقم مشخص شده در پرانتز را نمایش می دهد. که درخطوط 13، 14، 15، 16نمونه هایی از تبدیل نوع داده ضمنی نشان داده شده است، این نمونه ها از آن گرفته شده اندمیز 1. که در خطوط 17، 18یک راه برای تبدیل صریح داده ها را نشان می دهد. ماهیت این روش این است که شما باید یک کاما و صفر را به عدد صحیح اضافه کنید. که درخطوط 19، 20تبدیل صریح با استفاده از انواع castable به عنوان توابع انجام می شود که در داخل پرانتز آنها باید مقدار یا متغیری را که باید تبدیل شود مشخص کنید. خطوط 21، 22، 23 یک تبدیل نوع داده صریح را با استفاده از یک عملیات تبدیل داده واحد انجام می دهند. پرانتز نشان دهنده متغیر یا مقداری است که باید تبدیل شود و کاراکترها قاب می شوند < > نوع داده ای که باید به آن تبدیل شود. نمونه ای از نحوه عملکرد برنامه در زیر نشان داده شده است (شکل 1 را ببینید).

شکل 1 - تبدیل صریح و ضمنی انواع داده های C++

که در خطوط 22، 23یک عملیات تبدیل داده واحد انجام می شود و اعداد 15 و 20 به char تبدیل می شوند. این نوع داده هنوز برای شما شناخته نشده است، اما به یاد داشته باشید که char یک نوع داده برای ذخیره سازی است. بنابراین، از شکل 1می توانید ببینید که نمادها در انتها ظاهر می شوند. این کاراکترها با تبدیل اعداد به char به دست آمده اند. شماره ها کدهایی از . بنابراین، در صورت نیاز به خروجی هر کاراکتری از جدول ASCII، می توان آن را مطابق شکل انجام داد خطوط 22، 23، در حالی که فقط کد مورد نیاز را جایگزین می کند.

آخرین به روز رسانی: 1397/07/30

در فصل قبل در مورد تبدیل اشیاء از انواع ساده صحبت کردیم. حال به مبحث تبدیل اشیاء کلاس می پردازیم. فرض کنید سلسله مراتب کلاس زیر را داریم:

Class Person ( string public Name ( get; set; ) public Person(string name) ( Name = name; ) public void Display() ( Console.WriteLine ($"Person (Name))); ) ) class Employee: Person ( شرکت رشته عمومی ( دریافت؛ مجموعه؛ ) عمومی کارمند (نام رشته، شرکت رشته): پایه (نام) (شرکت = شرکت؛ )) کلاس مشتری: شخص (بانک رشته عمومی (به دست آوردن؛ مجموعه؛) مشتری عمومی (نام رشته، بانک رشته) : پایه (نام) (بانک = بانک؛ ))

در این سلسله مراتب کلاس، ما می توانیم زنجیره ارث بری زیر را دنبال کنیم: Object (همه کلاس ها به طور ضمنی از نوع Object ارث می برند) -> Person -> Employee|Client.

علاوه بر این، در این سلسله مراتب کلاس، انواع پایه در بالا و انواع مشتق شده در پایین قرار دارند.

تحولات از پایین به بالا به روز رسانی

اشیاء از نوع مشتق شده (که در پایین سلسله مراتب است) نیز نوع پایه را نشان می دهند. به عنوان مثال، یک شی Employee نیز یک شی از کلاس Person است. که در اصل طبیعی است، زیرا هر کارمند (کارمند) یک شخص (شخص) است. و برای مثال می توانیم اینگونه بنویسیم:

Static void Main(string args) ( Employee staff = new Employee("Tom", "Microsoft"); Person person = کارمند؛ // تبدیل از Employee به Person Console.WriteLine(person.Name); Console.ReadKey(); )

در این حالت، به متغیر person که نشان دهنده نوع Person است، یک مرجع به شی Employee اختصاص داده می شود. اما برای ذخیره ارجاع به یک شی از یک کلاس در متغیری از کلاس دیگر، لازم است یک تبدیل نوع - در این مورد، از نوع Employee به نوع Person انجام شود. و از آنجایی که Employee از کلاس Person ارث می برد، یک تبدیل ضمنی به سمت بالا به طور خودکار انجام می شود - تبدیل به نوعی که در بالای سلسله مراتب کلاس است، یعنی به کلاس پایه.

در نتیجه، متغیرهای کارمند و شخص به یک شی در حافظه اشاره می‌کنند، اما متغیر شخص فقط به بخشی دسترسی خواهد داشت که عملکرد نوع Person را نشان می‌دهد.

سایر تبدیلات صعودی به روشی مشابه انجام می شود:

Person person2 = مشتری جدید ("Bob"، "ContosoBank"); // تبدیل از مشتری به شخص

در اینجا، متغیر person2 که نوع Person را نشان می‌دهد، ارجاعی به شی Client را ذخیره می‌کند، بنابراین یک تبدیل ضمنی رو به بالا از کلاس مشتق‌شده Client به نوع Person پایه وجود دارد.

یک تبدیل ضمنی رو به بالا نیز در موارد زیر رخ خواهد داد:

Object person1 = کارمند جدید ("Tom"، "Microsoft"); // از Employee به شی شی person2 = new Client("Bob", "ContosoBank"); // از Client به شی شی person3 = new Person("Sam"); // از شخص به شی

از آنجایی که نوع شی، نوع پایه برای همه انواع دیگر است، تبدیل به آن به صورت خودکار انجام می شود.

تحولات نزولی پایین انداختن

اما علاوه بر تبدیل های رو به بالا از نوع مشتق شده به پایه، تبدیل های نزولی یا کاهشی - از نوع پایه به مشتق شده - وجود دارد. به عنوان مثال، در کد زیر، متغیر person یک مرجع به شی Employee ذخیره می کند:

کارمند کارمند = کارمند جدید ("تام"، "مایکروسافت"); شخص شخص = کارمند; // تبدیل از کارمند به شخص

و ممکن است این سوال پیش بیاید که آیا دسترسی به عملکرد نوع Employee از طریق متغیری از نوع Person امکان پذیر است؟ اما چنین تحولاتی به طور خودکار اتفاق نمی افتد، زیرا هر شخص (شیء شخص) کارمند شرکت (شیء کارمند) نیست. و برای تبدیل رو به پایین، باید یک تبدیل صریح اعمال کنید، که در داخل پرانتز نوع مورد نظر خود را به آن تبدیل کنید:

کارمند کارمند = کارمند جدید ("تام"، "مایکروسافت"); شخص شخص = کارمند; // تبدیل از Employee به Person //Employee staff2 = person; // این امکان پذیر نیست، شما نیاز به یک تبدیل صریح دارید Employee staff2 = (Employee)person; // تبدیل از شخص به کارمند

بیایید به چند نمونه از تبدیل نگاه کنیم:

// شی Employee نیز از نوع object object obj = new Employee("Bill", "Microsoft"); // برای دسترسی به قابلیت های نوع Employee، شی را به نوع Employee ارسال کنید Employee emp = (Employee) obj; // شی Client نیز نشان دهنده نوع Person Person person = new Client("Sam", "ContosoBank"); // تبدیل از Person به Client Client Client = (Client)person;

در حالت اول، به متغیر obj یک مرجع به شی Employee اختصاص داده می شود، بنابراین می توانیم شی obj را به هر نوع که در سلسله مراتب کلاس بین نوع شی و نوع Employee قرار دارد، تبدیل کنیم.

اگر نیاز به دسترسی به برخی خصوصیات یا متدهای منفرد یک شیء داشته باشیم، لازم نیست شیء تبدیل شده را به یک متغیر اختصاص دهیم:

// شی Employee نیز از نوع object object obj = new Employee("Bill", "Microsoft"); // تبدیل به نوع Person برای فراخوانی متد Display ((Person)obj).Display(); // یا مثل این // ((کارمند)obj).Display(); // تبدیل به Employee برای بدست آوردن ویژگی Company string comp = ((Employee)obj).Company;

در عین حال، هنگام ایجاد چنین تحولاتی باید مراقب بود. به عنوان مثال، در مورد زیر چه اتفاقی خواهد افتاد:

// شی Employee نیز از نوع object object obj = new Employee("Bill", "Microsoft"); // تبدیل به نوع Client برای دریافت ویژگی Bank string bank = ((Client)obj).Bank;

در این حالت با خطا مواجه خواهیم شد زیرا متغیر obj ارجاع به شی Employee را ذخیره می کند. این شی نیز یک شی از نوع object و Person است، بنابراین می توانیم آن را به آن انواع تبدیل کنیم. اما نمی توانیم به نوع Client تبدیل کنیم.

مثالی دیگر:

Employee emp = فرد جدید ("تام"); //! خطا Person person = new Person("Bob"); Employee emp2 = (Employee) person; //! خطا

در این حالت، ما سعی می کنیم یک شی از نوع Person را به نوع Employee تبدیل کنیم و شی Person یک شیء Employee نیست.

چندین راه برای جلوگیری از این خطاهای تبدیل وجود دارد.

روش های تبدیل

ابتدا می توانید از کلمه کلیدی as استفاده کنید. با استفاده از آن، برنامه سعی می کند یک عبارت را بدون پرتاب استثنا به یک نوع خاص تبدیل کند. اگر تبدیل ناموفق باشد، عبارت حاوی مقدار null خواهد بود:

شخص شخص = شخص جدید ("تام"); Employee emp = شخص به عنوان Employee; if (emp == null) ( Console.WriteLine ("تبدیل ناموفق")؛ ) other ( Console.WriteLine (emp.Company)؛ )

راه دوم گرفتن InvalidCastException است که در نتیجه تبدیل پرتاب می شود:

شخص شخص = شخص جدید ("تام"); امتحان کنید ( Employee emp = (کارمند) شخص؛ Console.WriteLine(emp.Company)؛ ) catch (InvalidCastException ex) ( Console.WriteLine(ex.Message)؛ )

راه سوم این است که با استفاده از کلمه کلیدی is معتبر بودن تبدیل را بررسی کنید:

شخص شخص = شخص جدید ("تام"); if(person is Employee) (Employee emp = (کارمند)شخص؛ Console.WriteLine(emp.Company)؛ ) else (Console.WriteLine("تبدیل مجاز نیست")؛ )

عبارت person is Employee تست می کند که آیا متغیر person یک شی از نوع Employee است یا خیر. اما از آنجایی که در این مورد واضح است که چنین نیست، چنین چکی نادرست است و تبدیل کار نخواهد کرد.

در برنامه نویسی اغلب نیاز به انجام محاسبات روی متغیرهای یک نوع و در نتیجه بدست آوردن نوع دیگری وجود دارد. به عنوان مثال، هنگام تقسیم اعداد صحیح، یک عدد واقعی بدست آورید. هنگامی که مقداری از یک نوع در متغیری از نوع دیگر ذخیره می شود، تبدیل نوع داده انجام می شود. در سی شارپ این با چندین مکانیسم پیاده سازی می شود.

تبدیل نوع داده ضمنی

تبدیل ضمنی زمانی اتفاق می‌افتد که یک مقدار در متغیری از نوع متفاوت ذخیره شود بدون اینکه در کد مشخص شود.

برای اینکه کامپایلر سی شارپ به طور خودکار یک نوع داده را تبدیل کند، دو شرط باید درست باشد:

  • نوع مقدار و نوع متغیر باید با یکدیگر سازگار باشند.
  • محدوده مقادیر ممکن یک نوع متغیر نباید کمتر از یک نوع مقدار باشد.

بایت a = 15; بایت b = 120; int c = a + b;

در این مثال ما انجام می دهیم تبدیل ضمنی ، و متغیر c در نهایت از نوع int خواهد بود. این امکان پذیر است زیرا بایت و int مقادیر صحیح هستند و نوع int به طور کامل شامل محدوده بایت می شود.

یک مثال دیگر:

کوتاه a = 815; ushort b = a;//خطا

حتی اگر عدد 815 در محدوده کوتاهی قرار دارد، در این مورد یک پیام خطا ظاهر می شود. این به این دلیل است که کامپایلر برای تبدیل ضمنی فقط شرایط فوق را بررسی می کند و بدون توجه به مقادیر خاص فقط به انواع نگاه می کند.

تبدیل نوع داده صریح

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

Int d = 13; int c = 4; var e = (دو برابر)d/c;

بنابراین، متغیر e دارای نوع double و مقدار 3.25 خواهد بود. اگر از تبدیل استفاده نمی کردیم، این متغیر طبق قوانین تقسیم اعداد صحیح در سی شارپ دارای نوع int و مقدار 3 خواهد بود.

مثالی دیگر:

Ushort num = 257; دو برابر dbl = 34.9318; int rnd = (کوتاه) dbl; // به مقدار 34 بایت اختصاص داده می شود what = (byte) num; // مقدار 1 به آن اختصاص داده می شود

در مورد rnd، قانون زیر اعمال می شود: هنگام تبدیل اعداد ممیز شناور به اعداد صحیح، قسمت کسری آنها کنار گذاشته می شود.

و در مورد چه چیزیهمه چیز کمی پیچیده تر است هنگام انجام یک تبدیل صریح، کامپایلر به محدوده های نوع توجه نمی کند. و در حین اجرای برنامه، اگر سعی شود مقداری در متغیری که در محدوده آن نیست (با عمق بیت بیشتر) وارد شود، تمام بیت های مرتبه بالا پاک می شوند. عدد 257 در نمایش باینری کوتاه مانند 00000001 00000001 به نظر می رسد. وقتی به بایت تبدیل می شود، تنها آخرین بایت 00000000، یعنی 1، باقی می ماند.

تبدیلی که در آن محدوده نوع متغیر به طور کامل محدوده نوع مقدار را پوشش نمی دهد، تبدیل باریک نامیده می شود. برای جلوگیری از از دست رفتن داده ها، مانند مثال قبلی، باید از چک سرریز استفاده کنید.

کاهش سرریز تبدیل داده ها

بدیهی است که نمی‌توانید مکان‌هایی را در کد که ممکن است از دست دادن داده‌ها به دلیل سرریز رخ دهد نادیده بگیرید. در غیر این صورت، متغیرها ممکن است مقادیر غیرمنتظره ای بگیرند که بر عملکرد برنامه تأثیر می گذارد و ردیابی علت خطا بسیار دشوار خواهد بود.

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

تصادفی rnd = new Random(); int بزرگتر = rnd.Next(99999); // یک عدد تصادفی از 0 تا 99999 کوچکتر ایجاد می کند. try ( کوچکتر = علامت خورده ((کوتاه) بزرگتر)؛ ) catch (System.OverflowException e) ( Console.WriteLine('Overflow raised by checked on smaller: ' + e.ToString()); // چاپ //پیامی درباره که یک استثنا کوچکتر بود = -1؛ )

بزرگتر یک عدد تصادفی از 0 تا 99999 است. اگر این مقدار از محدوده نوع کوتاه بیشتر شود، یک استثنا ایجاد می شود. System.OverflowExceptionو متغیر کوچکترمقدار -1 به آن اختصاص داده خواهد شد. اگر سرریز اتفاق نیفتد، استثنایی وجود نخواهد داشت و متغیر کوچکتر به سادگی مقدار متغیر بزرگتر را می گیرد، اما در نوع کوتاه.

اپراتور است

برای بررسی اینکه آیا یک شی را می توان به یک نوع خاص در سی شارپ فرستاد، از عملگر استفاده می شود است. بررسی می کند که آیا یک شی نمونه ای از نوع مشخص شده خود است یا مشتقی از آن. نتیجه یک عملیات باینری با عملگر is یک مقدار بولی (درست یا نادرست) است.

مثالی از استفاده از عملگر is:

کلاس Samle1 () کلاس Samle2 () : Sample1 () // کلاس Sample1 کلاس Sample3 () Samle1 t1 = new Samle1(); Sample2 t2 = new Sample2(); Sample3 t3 = new Sample3(); char t4 = 't'; if (t1 is Sample1) ( Console.WriteLine("t1 is Sample1")؛ ) // اگر (t2 is Sample1 است) ( Console.WriteLine("t2 is Sample1"); ) // چاپ می شود اگر ( t3 is Sample1) ( Console.WriteLine ("t3 is Sample1"); ) // اگر (t4 Sample1 باشد) چاپ نمی شود ( Console.WriteLine("t4 is Sample1"); ) // چاپ نمی شود

اپراتور به عنوان

عملگر as برای تبدیل انواع مرجع سازگار استفاده می شود. لازم به ذکر است که در صورت عدم امکان تبدیل، استثنا پرتاب نمی شود، اما null برگردانده می شود. این باید هنگام پردازش نتیجه تبدیل با استفاده از as در نظر گرفته شود.

شیء کلاس SamleClass() Arr = شیء جدید; Arr = new SampleClass(); Arr = "خوش آمدید"؛ Arr = (کوتاه) 654; Arr = null; برای (بایت i = 0؛ i< Arr.Length; i++) { string t1 = Arr[i] as string; SampleClass t2 = Arr[i] as SampleClass; if (t1 != null) { Console.WriteLine(“{0}: it’s a string ‘” + t1 + “’”, i+1); } else if (t2 != null) { Console.WriteLine(“{0}: it’s a SampleClass”, i+1); } else { Console.WriteLine(“{0}: it’s not string or SampleClass”, i+1); } }

خروجی مثال به صورت زیر خواهد بود:

1: این یک کلاس نمونه است

2: این یک رشته "خوش آمدید" است

3: رشته یا SampleClass نیست

4: رشته یا SampleClass نیست

تبدیل انواع با استفاده از کلاس Convert

کلاس System.Convert سیستم حاوی متدهایی است که می توان از آنها برای تبدیل مقادیر انواع داده های پایه و همچنین نوع سیستم DateTime استفاده کرد.

تبدیلشامل مجموعه ای از روش ها مانند ToType، جایی که Type با نام سیستم نوع مقدار هدف جایگزین می شود (به عنوان مثال ToChar، ToInt32، ToBoolean). این روش ها به شما این امکان را می دهند که تمام تبدیل های ممکن را از انواع پایه C# انجام دهید.

متد Convert.ChangeType هر شی را به هر نوع مشخصی تبدیل می کند.اگر عدم تطابق نوع یا سرریز وجود داشته باشد، متد یک استثنا ایجاد می کند.

بهترین مقالات در این زمینه