1. مهمان گرامی، جهت ارسال پست، دانلود و سایر امکانات ویژه کاربران عضو، ثبت نام کنید.
    بستن اطلاعیه

آموزش جامع تصویری و کاربردی سی شارپ (#c)

شروع موضوع توسط minaaa ‏10/11/11 در انجمن C #C++

  1. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    Overriding in CSharp

    همانطور که قبلا گفتم تمامی کلاس ها در سی شارپ چه بخواهند و چه نخواهند از کلاسی به نام object به ارث می روند و در نتیجه خصوصیات این کلاس به آن ها ارث می رسد. به عنوان مثال متد ToString که در تمامی کلاس هایی که ما ایجاد می کنیم وجود دارد و وقتی روی یک شیء متد ToString را فراخوانی می کنیم یک String از آن شیء در اختیار ما قرار می دهد که به صورت پیش فرض این رشته نام کامل کلاس شامل Namespace.Class می باشد.

    کد:

    Person p = new Person(); p.Name = "Ali"; p.Age = 20; Console.WriteLine(p); // ConsoleApplication13.Person

    در مثال بالا من یک شیء از کلاس Person ایجاد کرده و بعد از ست کردن نام و سن دستور چاپ آن شیء را از طریق متد WriteLine ارسال کرده ام. با توجه به اینکه متد WriteLine در موقع چاپ اشیاء به یک String نیاز دارد متد ToString را بر روی شیء من فراخوانی خواهد کرد. در نتیجه یک رشته از شیء من چاپ خواهد شد که نام کامل کلاس یعنی : ConsoleApplication13.Person می باشد.

    به شکل زیر دقت کنین:

    [​IMG]

    همانطور که می بینین متد ToString از کلاس پدر که کلاس object است به من به ارث رسیده است. اما رفتار این متد رفتاری نیست که من نیاز داشته باشم به این معنا که من نیاز دارم وقتی متد ToString را روی اشیایی از جنس کلاس Person (که من ایجاد کردم) فراخوانی می شود به جای نام کامل کلاس (Qualified Name) اطلاعات آن را (یعنی نام و سن) در اختیار من قرار دهد. به این معنی که می خواهم رفتارهای کلاس پدر را تغییر دهم که اصطلاحا به این کار Overriding می گویند.

    برای اینکه من بتوانم رفتار کلاس پدر را تغییر دهم باید متد مورد نظرم را override کنم که این کار با استفاده از عبارت override و نوشتن مجدد متد با رفتار مورد نظر خودمون امکان پذیر می باشد:

    کد:

    public class Person { public string Name; public int Age; override public string ToString() { return string.Format("Name:{0}, Age:{1}",Name,Age); } }
    بعد از نوشتن این تکه کد اگر دوباره کدی که در ابتدا نوشتیم را اجرا کنیم با رفتار جدید متد ToString که در واقع چاپ نام و سن می باشد مواجه خواهید شد.


    Virtual Methods in CSharp

    اصولا وقتی یک کلاس ایجاد می کنیم باید در نظر داشته باشیم که اگر این کلاس توسط کلاس دیگری به ارث رفت کدامیک از متد ها یا Property های آن توسط فرد دیگری استفاده خواهند شد و اگر کسی در کلاس جدید نیاز به تغییر رفتار داشت این امکان را در اختیار وی قرار دهیم.


    فرض بفرمائید که من یک کلاس به نام Person با دو Propertyی نام و سن و یک Method به نام Print که نام و سن را چاپ می کند ایجاد کرده ام. حالا می خواهم کاری کنم که کلاس هایی که از کلاس Person به ارث می روند بتوانند رفتار متد Print را override کنند. برای اینکه این امکان را در اختیار فرزندانم (کلاس هایی که از من به ارث می روند) قرار دهم باید در تعریف متد از عبارت virtual استفاده کنم. به کد کلاس Person دقت کنین:


    کد:

    public class Person { public string Name; public int Age; public virtual void Print() { Console.WriteLine("Name: {0}, Age: {1}", Name, Age); } }

    حالا کلاس Emp را از کلاس Person به ارث می برم و یک فیلد جدید به نام Salary به آن اضافه می کنم و انتظار دارم که با override کردن متد Print کاری کنم که وقتی Print روی اشیایی از جنس Emp فراخوانی می شوند علاوه بر نام و سن , حقوق را نیز چاپ کند.

    کد:

    public class Emp : Person { public decimal Salary; override public void Print() { Console.WriteLine("Name:{0}, Age: {1}, Salary: {2}",Name,Age,Salary); } }
    با توجه به کد بالا در صورتیکه که این کد را برای تست بنویسم باید علاوه بر نام و سن , حقوق کارمند را هم چاپ نماید:

    کد:

    Emp e = new Emp(); e.Name = "Reza"; e.Age = 25; e.Salary = 240000; e.Print();


    !! نکته بسیار مهم در مورد Overriding این است که در صورتیکه Reference شما به یک شیء از جنس پدر نیز باشد , کامپایلر سی شارپ بدون توجه به نوع Reference به ماهیت شیء توجه کرده و متد مربوطه را چاپ می نمایند. به کد زیر دقت کنین:


    کد:

    Emp e = new Emp(); e.Name = "Saeid"; e.Age = 44; e.Salary = 54000; // در این خط از کد عملیات UpCast به سادگی انجام می شود Person p = e; // فراخوانی متدی که در کلاس پدر وجود داشته و در کلاس فرزند override شده است p.Print();
    با اینکه reference ما به شیء از جنس پدر(Person) می باشد ولی به دلیل override شدن در کلاس فرزند , پیاده سازی متد فرزند یعنی چاپ نام , سن و حقوق اجرا می شود.​
     
  2. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    چند ريختي در سي شارپ

    بررسي مفهوم overriding را با يك مثال پيگيري مي كنيم. يك سازمان يا شركت را در نظر بگيريد. اين شركت داراي دو نوع كارمند مي باشد. نوع اول كارمندان حقوق بگيري هستند كه حقوقشان را به صورت ماهيانه و با توجه به پايه حقوقي ثابتي كه برايشان در نظر گرفته شده است دريافت مي كنند. به عنوان مثال "علي رضايي"‌ يك كارمند حقوق بگير است كه براي هر ماه مبلغ "100,000" دريافت مي كند.

    نوع دوم كارمنداني هستند كه به صورت ساعتي حقوقشان را دريافت مي كنند و براي هر ساعت كار يك مبلغ مشخصي دريافت مي كنند. به عنوان مثال "رامين احمدي" يك كارمند ساعتي است كه براي هر ساعت كار مبلغ "3000" دريافت مي كند.

    در اين مثال باتوجه به اينكه تمامي كارمندان داراي اطلاعات مشتركي هستند (مثل نام و شماره كارمندي و اطلاعات سوابق و ....)‌ تصميم گرفتيم يك كلاس پايه به نام Emp كه مخفف Employee‌ است در نظر بگيريم و اطلاعات مشترك را در اين كلاس تعريف كنيم:
    [​IMG]

    همانطور كه مي بينين با توجه به اينكه مفهوم حقوق براي كلاس كارمند (بدون مشخص بودن نوعش)‌ يك مفهوم انتزاعي است من در اين مثال حقوق (يا همان Salary) را به صورت virtual‌ تعريف كرده ام , تا كلاس هايي كه از كلاس Emp به ارث مي روند با override‌ كردن اين Property‌ پياده سازي درست آن را در نوع خود انجام دهند.


    پس دو كلاس MonthlyEmp و HourlyEmp را كه از كلاس پايه Emp به ارث رفته اند به صورت زير تعريف خواهند شد:
    [​IMG]
    در كلاس MonthlyEmp‌ (كه در واقع كارمند حقوق بگير ماهيانه مي باشد) حقوق بر اساس "پايه حقوق" محاسبه مي شود.

    [​IMG]

    همانطور كه در كد مي بينيد , در كلاس HourlyEmp‌‌ (كه همان كارمند ساعتي است) حقوق براساس "مبلغ پايه هر ساعت" * "تعداد ساعات كاركرد" محاسبه و پرداخت و خواهد شد.


    مثال را بايد ايجاد يك كلاس چهارم به نام Company تكميل مي كنيم. در اين كلاس يك آرايه از كارمند (Emp) داريم. دليل اينكار كاملا آشكاراست. چون احتمال ايجاد كلاس هاي جديد (در واقع نوع هاي كارمندان جديد) وجود دارد در نتيجه من يك آرايه از كلاس پدر كه همان Emp‌ است براي نگهداري ليست كارمندان ايجاد مي كنم.

    [​IMG]

    همانطور كه در كد مي بينين يك متد به نام PaySalary در اين كلاس ايجاد شده كه در واقع هر ماه يكبار توسط مدير عامل شركت جهت پرداخت حقوق تمامي كارمند استفاده مي شود. صرف نظر از اينكه در موقع فراخواني واقعا چه نوع كارمندي در اين آرايه پر شده است , انتظار من اين است كه اگر كارمند ساعتي بود از روش محاسبه كارمند ساعتي و اگر كارمند حقوق بگير بود از روش محاسبه كارمند حقوق بگير حقوق افراد پرداخت شود. اين دقيقا همان نكته ايست كه در فقط در مواقعي كه شما از overriding‌استفاده كنين اتفاق خواهد افتاد. به عبارت ديگر "در overriding صرف نظر از نوع ديدگاه ما (reference) به يك شيء‌ , ماهيت آن مشخص كننده فراخواني متد پدر يا فرزند خواهد بود" يعني اگر حتي reference ما به يك شيء‌ MonthlyEmp‌ از نوع Emp‌ باشد (يعني عمل upcase اعمال شده باشد) باز در مواقع فرخواني متد , پياده سازي فرزند مورد استفاده قرار خواهد گرفت. اصولا اين عمل را در دنياي برنامه نويسي شيء‌گرا "چند ريختي" يا Polymorphysm‌ مي گويند.
     
  3. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    Method Hiding in CSharp

    پس بررسي مفهوم overriding‌ خوب است كه كمي در رابطه با مفهوم Hiding‌ هم صحبت كنيم. در واقعا Hiding‌ دوباره نويسي يك متد است كه قبلا در كلاس پدر نوشته شده. اما نكته اي كه وجود دارد اين است كه در Hiding‌ كامپايلر سي شارپ با توجه به نوع Reference‌ شما متد را اجرا مي كند.
    اگر مثال آخرين پست را بررسي كنيم متوجه مي شويم كه در آن مثال ما يك ليستي داشتيم از Emp ها (يك آرايه از Emp) كه در حقيقت اشيايي از MonthlyEmp‌ و HourlyEmp‌ را داخلشان قرار مي داديم. در واقع ما اشيايي از جنس كلاس هاي فزرند داشتيم اما به Reference‌هايي از جنس كلاس هاي پدر. نكته حائز اهميت اين است كه وقتي روي اين اشياء‌ متد ShowInfo‌ را فراخواني مي كرديم. با اينكه ديد (Reference) ما به اشياء‌ از نوع Emp بود ولي Salary را باتوجه به ماهيت اشياء (چيزي كه با آن new‌شده بودند)‌فراخواني مي كرد و به نوع Reference‌ ما اهميت نمي داد. با استفاده از همين خاصيت ما Polymorphism را در مثال قبل پياده سازي كرديم.
    Hiding در واقع نقطه مقابل Overriding است. به اين ترتيب كه شما بدون توجه به ماهيت شيء (چيزي كه new شده است)‌و صرفا با توجه به نوع Reference پياده سازي مربوطه را فرخواني مي كنين.

    در مثال زير من يك كلاس به نام Person‌ دارم كه متدي به نام Print را پياده سازي كرده است.
    يك كلاس ديگر به نام Student از كلاس Person‌ به ارث رفته و باز هم متد Print را پياده سازي كرده است. دقت بفرمائيد كه در Hiding هنگام پياده سازي مجدد از كلمه كليدي new استفاده خواهيم كرد.

    [​IMG]

    پس در صورتيكه من يك شيء از جنس Student بسازم ولي ديدم را به Person‌ محدود كنم. وقتي متد Print را فراخواني مي كنم پياده سازي كه در كلاس Person وجود دارد فراخواني مي شود. همانطور كه گفتيم در Hiding كامپايلر با توجه به Reference‌ ما متد مورد نظر را فراخواني مي كند و به ماهيت شيء توجهي نمي كند.

     
  4. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    فراخواني سازنده ها - Constructor Calling

    وقتي از يك كلاس كه يك كلاس ديگر به ارث رفته است , شيء‌ مي سازيم در واقع متد سازنده آن كلاس و تمامي كلاس هايي كه به عنوان پدر اين كلاس مطرح هستند را نيز فراخواني مي نمائيم. به عنوان مثال كلاس Customer از كلاس Person به ارث رفته است. در كلاس Person من دو نوع Constructor‌ دارم. يكي همان Default Constructor است كه به صورت public و بدون پارامتر تعريف شده و ديگر داراي دو پارامتر است. يكي از جنس String‌ كه نام فرد است و ديگري از جنس int كه سن فرد مي باشد:

    کد:

    public class Person { public int Age; public string Name; public Person() { Console.WriteLine("Default Constructor of Person Called"); } public Person(string Name, int Age) { this.Name = Name; this.Age = Age; Console.WriteLine("2nd Constructor of Person Called"); } public virtual void Print() { Console.WriteLine("Name: {0}, Age: {1}", Name, Age); } }

    همانطور كه مي بينيد من براي اينكه مشخص بشه كه از كدام Cosntructor‌ استفاده مي شود در هر دو Constructor‌ يك جمله چاپ مي كنم.

    حالا كلاس Customer‌ را از كلاس Person‌ به ارث مي بريم:

    کد:

    public class Customer : Person { public decimal Credit; override public void Print() { Console.WriteLine)"Name: {0}, Age: {1}, Credit: {2}", Name, Age, Credit); } }

    حالا براي تست يك شيء از كلاس Customer ايجاد مي كنم:

    کد:

    Customer C = new Customer(); c.Name = "Ali"; c.Age = 20; c.Credit = 2000; c.Print();
    كه در نتيجه در محيط Console‌ خروجي شبيه به اين خواهيم داشت:

    [​IMG]

    همانطور كه در خروجي هم مشخص شده است , با اينكه من Constructor كلاس فرزند را فراخوني كردم اما Default Constructor كلاس پدر نيز فراخواني شده است.

    نكته اي كه وجود دارد اين است كه وقتي شما مشخص نكنين كه كدام ‍Constructor‌ از كلاس پدر فرخواني شود سازنده پيش فرض كلاس پدر فراخواني خواهد شد.

    اما در صورتيكه نخواهيم سازنده پيش فرض فراخواني شود بايد چه كنيم؟ يا اگر در كلاس پدر سازنده پيش فرض نداشتيم چطور؟

    در صورتيكه شما مي خواهيد يكي از سازنده هاي پدر را صراحا خودتان اعلام كنين كافي است كه در مقابل تعريف سازنده خود از كلمه base استفاده كنين:

    کد:

    public Customer(string Name, int Age, decimal Credit): base(Name,Age) { this.Credit = Credit; Console.WriteLine("Customer Constructor called"); }

    حالا اگر مجددا يك شيء از كلاس Customer ايجاد كنين , نتيجه اي متفاوت خواهيد داشت:

    [​IMG]

    همانطور كه در تصوير خروجي هم مشخص است. ابتدا سازنده پدر فرخواني شده (كه البته با اين روش من كد كمتري هم نوشته ام و از كدي كه سازنده پدر وجود دارد استفاده مجدد كرده ام) و بعد سازنده كلاس Customer
     
  5. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    بازنويسي عملگرها در سي شارپ - Operator Overloading in csharp

    تمامي عملگرها (operators) در سي شارپ داراي رفتار هاي از پيش تعيين شده اي هستند و شما مي توانيد از اين عملگرها در عبارت هاي خود استفاده کنين:

    کد:

    int i = 10; int j = 20; int m = i * j / 2 + 14; Console.WriteLine("m is :{0}",m);
    اما اگر شما عبارت زير را بنويسيد چطور؟

    کد:

    Person p = new Person("Ali",20); Person p2 = new Person("Reza",30); Person p3 = p + p2; Console.WriteLine("Name: {0}, Age:{1}",p3.Name , p3.Age);

    در صورتيکه اين کد را Compile کنين متوجه يک خطاي Compile Time خواهيد شد که به شما توضيح مي دهد که امکان جمع بستن دو Person با يکديگر وجود ندارد يا به عبارت ديگر عملگر + براي Person تعريف نشده است.
    در سي شارپ شما مي توانين بسياري از عملگرها را دوباره نويسي کنين به عبارت ديگر شما مي توانين در مواقع لزوم تعريف جديد از يک عملگر در سي شارپ داشته باشيم. در تصوير زير ليست عملگرها به همراه توضيحاتي راجع به امکان بازنويسي شان مي بينيد.
    [​IMG]


    همانطور که در تصوير مشخص است شما نمي توانين تمام عملگرها را دوباره نويسي کنين.
    در سي شارپ يک کلمه کليدي به نام operator وجود دارد که براي بازنويسي عملگرها بايد از آن استفاده کنيم. به عنوان مثال براي اينکه مثالي که در ابتدا نوشتيم درست عمل کند و وقتي عبارت بالا را اجرا مي کنيم يک شيء جديد از جنس Person ايجاد شود که نامش از جمع بستن نام اين دو فرد و سنش از جمع بستن سن اين دو فرد تشکيل شود من در کلاس Person اين کد را مي نويسم:

    کد:

    public static Person operator +(Person p1, Person p2) { Person p = new Person(); p.Name = p1.Name + " " + p2.Name; p.Age = p1.Age + p2.Age; return p; }
    دقت بفرمائيد که حاصل جمع دو شيء از جنس Person يک Person مي باشد و من در پياده سازي عملگر + يک فرد جديد ساخته ام. اما اگر بخواهيم عملگر == يا همان برابري را دوباره نويسي کنيم بايد دقت کنيم که خروجي آن بايد يک عبارت true /false و از جنس bool باشد. در نتيجه اگر من بخواهم که مبناي مقايسه دو شيء از جنس Person را براساس نامشان قرار دهم از اين کد استفاده مي کنم:

    کد:

    public static bool operator ==(Person p1, Person p2) { return p1.Name == p2.Name; }
    نکته مهم در اين مثال اين است که شما وقتي عملگر == (برابري) را دوباره نويسي مي کنين بايد عملگر != (نابرابري) را هم دوباره نويسي کنين:

    کد:

    public static bool operator !=(Person p1, Person p2) { return !(p1==p2); }
     
  6. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    مثال برای overload کردن عملگرها
    با اعداد کامپلکس که آشنا هستید ، a+bi که a, b عدد حقیقی هستند و i= جذر -1

    کد:

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Numbers { public class ComplexNumber { double _Real = 0; double _Image = 0; public double Image { get { return _Real; } set { _Real = value; } } public double Real { get { return _Image; } set { _Image = value; } } public ComplexNumber() { } public ComplexNumber(double RealPart, double ImagePart) { Real = RealPart; Image = ImagePart; } public ComplexNumber(int RealPart, int ImagePart) { Real = (double)RealPart; Image = (double)ImagePart; } public override string ToString() { return string.Format("{0:G} + {1:G}i", _Real, _Image); } public static ComplexNumber operator +(ComplexNumber A, ComplexNumber B) { ComplexNumber c = new ComplexNumber(); c.Real = A.Real + B.Real; c.Image = A.Image + B.Image; return c; } public static ComplexNumber operator -(ComplexNumber A, ComplexNumber B) { ComplexNumber c = new ComplexNumber(); c.Real = A.Real - B.Real; c.Image = A.Image - B.Image; return c; } public static ComplexNumber operator *(ComplexNumber A, ComplexNumber B) { ComplexNumber c = new ComplexNumber(); c.Real = (A.Real * B.Real) - (A.Image * B.Image); c.Image = (A.Image * B.Real) + (A.Real * B.Image); return c; } public static ComplexNumber operator /(ComplexNumber A, ComplexNumber B) { ComplexNumber c = new ComplexNumber(); double s; s = B.Real * B.Real + B.Image * B.Image; c.Real = ((A.Real * B.Real) + (A.Image * B.Image)) / s; c.Image = ((A.Image * B.Real) - (A.Real * B.Image)) / s; return c; } } }
    و برای استفاده از این کلاس

    کد:

    ComplexNumber c1 = new ComplexNumber(1, 1); ComplexNumber c2 = new ComplexNumber(4d, 9d); MessageBox.Show((c1 + c2).ToString()); MessageBox.Show((c1 - c2).ToString()); MessageBox.Show((c1 * c2).ToString()); MessageBox.Show((c1 / c2).ToString());
     
  7. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    كنترل خطاها در سي شارپ - Exception Handling in CSharp
    در تمامي زبان هاي برنامه نويسي روش هايي براي مقابله با خطا ها وجود دارد. عموما خطا ها را از ديد زمان وقوع به دو دسته Compile Time Errors و RunTime Error ها تقسيم كرد. خطا هاي گروه اول يا همان خطاهاي زمان كامپايل توسط Compiler تشخيص داده و به كاربر نمايش داده مي شوند. خطاهايي از قبيل استفاده از يك متغييري كه مقدار دهي نشده است يا اشتباه در Syntax و ....

    خطاهاي زمان اجرا عموما خطاهايي هستند كه در زمان كامپايل توسط Compiler تشخيص داده نشده اند و در زمان اجراي نرم افزار بروز مي كنند. خطاهایی مثل مشکل در اتصال به بانک اطلاعاتی , ورود اطلاعات اشتباه توسط کاربر , عدم دسترسی به فایل مورد نظر و ....
    اصولا هر برنامه نویس در هنگام نوشتن خطوط کد خود می تواند احتمال وقوع خطا را پیش بینی کند مثلا در مثال زیر من از کاربر انتظار دارم تا یک عدد را برای من تایپ کند:

    [​IMG]

    اما می توانم حدس بزنم که کاربر می تواند به جای 10 کلمه "ALI" را سهوا یا عمدا تایپ نماید. که در این صورت نرم افزار من دچار اشکال شده و از برنامه خارج خواهد شد.

    در سی شارپ برای اینکه بتوانیم خطا ها را کنترل کنیم , خطوطی را که احتمال وقوع خطا در آن ها زیاد است را در try catch می نویسیم.
    برای اینکار کافی است که به صورت زیر عمل کنیم:
    [​IMG]


    همانطور که می بینید من خطوطی از کد که احتمال خطا دارد را داخل بلاک try قرار دادم و عکس العمل خودم در موقع بروز خطا را نیز در بلاک catch. در واقع در صورتیکه در هر یک از خطوط داخل block try دچار خطا شویم به قسمت catch ارجاع داده خواهیم شد و می توانیم آنجا عکس العمل لازمه را نشان دهیم.

    در سی شارپ و در namespace ی به نام System یک کلاس به نام Exception وجود دارد که در حقیقت base classی برای تمام انواع خطا ها در سی شارپ می باشد. به این معنا که تمامی خطاهایی که در سی شارپ وجود دارند از Exception به ارث رفته اند و در نتیجه تمامی آنها به نوع Exception می باشند. برای اینکه بتونین لیست Exception ها را ببینین کافیه که از منوی Debug گزینه Exception را کلیک کنین تا لیست Exception ها را به تفکیک namespace ملاحظه بفرمائید. (می تونین از Alt + Ctrl + E به عنوان Shortcut استفاده کنین).

    [​IMG]

    با توجه به مثال اولی که نوشتیم تا اینجا ما توانستیم در مواقعی که احتمال وقوع خطا وجود دارد با استفاده از try و Catch مانع از بسته شدن نرم افزارمان یا به اصطلاح crash شدن آن شویم. مرحله بعدی تشخیص دادن نوع خطا و در نهایت نشان دادن عکس العمل مناسب در مقابل خطای مورد نظر است.

    تولید خطا در سی شارپ
    اما قبلا از اینکه به این موارد بپردازیم اجازه بدین بررسی کنیم که در سی شارپ چطور می توانیم تولید خطا کنیم؟
    برای ایجاد یک خطا در زمان runtime در سی شارپ کافی است که یک شیء از جنس Exception را بوسیله کلمه کلیدی throw پرتاب کنیم. به مثال زیر دقت کنین:

    [​IMG]
    در نتیجه کد بالا که در واقع تولید یک خطا را نمایش می دهد خطای زیر از نرم افزار ایجاد شده و نرم افزار بسته خواهد شد:

    [​IMG]
    در واقع وقتی شما با یک خطا برخورد می کنید به این معنی است که یک شیء از جنس Exception یا کلاس هایی که از Exception به ارث رفته اند توسط کلمه کلیدی throw پرتاب شده است. حالا اگر شما از block های try , catch استفاده کنین می توانین در مقابل آن خطا عکس العمل نشان دهید:
    [​IMG]
    در تصویر بالا من هنگام پرتاب کردن خطا یک متن را به عنوان توضیح خطا در سازنده کلاس Exception قرار داده ام که این متن را بعد ها از طریق متغییر Message می توانم به دست بیاورم. اما نکته ای که وجود دارد این است که برای اینکه بتوانید متن خطا و محل آن را به دست بیاورید به آن شیء ای که پرتاب شده است نیاز دارید. پس من با این یک متغییر به آن شیء دسترسی پیدا می کنم:

    همانطور که می بینین متنی که در شیء پرتاب شده اعلام شده است در داخل متغییر Message در شیء ex قرار گرفته است و من می توانم آن را نمایش دهم.

    همچنین شما می توانین از طریق متغییر StackTrace کلاس Exception مسیر اتقاقات رخ داده تا زمان بروز خطا را در غالب یک رشته داشته باشین:

    [​IMG]
     
  8. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    تشخيص نوع خطا توسط Catch

    همانطور كه در قسمت قبل اشاره شد شما مي توانيد با استفاده از Try Catch‌ در مقابل خطاي احتمالي عكس العمل نشان دهيد. حالا به مثال زير دقت كنين:


    [​IMG]

    در اين مثال كاربر شما بايد دو عدد را تايپ كرده و نرم افزار اين اعداد را كه در غالب رشته اي (string) از متد ReadLine كلاس Console ‌گرفته شده اند - و بعد با استفاده از متد Parse‌ به عدد تبديل گشته اند - را بر هم تقسيم كرده و نتيجه را به شما نشان مي دهد.
    با توجه به كد بالا من مي توانم احتمال بروز دو نوع خطا را تشخيص دهم:
    1. كاربر به جاي تايپ كردن يك عدد از رشته ها استفاده كند (مثلا بنويسد Ali)
    2. كاربر يك عدد را بر 0 تقسيم نمائيد (‌در دات نت و بيشتر زبان هاي برنامه نويسي هيچ عددي را بر 0 نمي توان تقسيم كرد و در صورتيكه اين كار را انجام دهيم يك خطا از نوع DividedByZeroException پرتاب خواهد شد)

    [​IMG]

    نكته اي كه وجود دارد اين است كه من مي خواهم در مقابل هريك از اين نوع هاي خطا عكس العمل مناسب خودش را نشان دهم. براي اينكه بتوانم اين كار را انجام دهم بايد از چندين قسمت Catch استفاده كنم و در هر قسمت يك نوع از خطا ها را كنترل كنم:

    [​IMG]

    همانطور كه مي بينيد من در ابتدا خطاي نوع FormatException را كنترل مي كنم كه در مواقعي Raise مي شود كه شما يك رشته نا صحيح را با عدد تبديل كنين. مثالا سعي كنين حرف ABD را به عدد تبديل كنين.
    در قسمت دوم من يك خطا از نوع DividedByZeroException را كنترل مي كنم كه در مواقعي ايجاد مي شود كه شما يك عدد را بر 0 تقسيم نمائيد. و در نهايت در سومین Catch هر نوع خطا ديگري كه در اين دو نوع قرار نگيرد را کنترل و یک متن عمومی را نمایش می دهد. در واقع شما می توانین با استفاده از چند قسمت Catch هر نوع خطای احتمالی را گرفته و عکس العمل مناسب در مقابل آن نمایش دهید.

    به منظور دریافت متن اصلی خطا و اطلاعات دیگر خطای اصلی , شما می توانید در مقابل هر یک یک متغییر تعریف کرده و از اطلاعات آن استفاده نمائید.
     
  9. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    ايجاد خطاهي خاص - Custom Exception Definition
    خيلي اوقات شما مي خواهيد همراه با اعلام خطا اطلاعات ديگري را كه فقط هنگام ايجاد خطا در اختيارتان هست را هم داشته باشيد و اعلام نمايد. براي اين منظور بايد يك كلاس جديد ايجاد كرده و آن كلاس را از كلاس Exception به ارث ببريد. سپس اطلاعات اضافه مورد نياز خود را در آن كلاس به صورت ReadOnly Property تعريف كرده و آن ها را با استفاده از Constructor كلاستان مقدار دهي نمايد.

    [​IMG]

    ! همانطور كه مي بينيد من در زمان ايجاد شدن شيء از اين كلاس مقدار message‌ را به Contrcutor كلاس پدر پاس مي كنم.

    در مثال زير يك كلاس به نام Person‌ وجود دارد. تصميم گرفتم كه وقتي مقداري بيش از 100 و يا كمتر از 0 براي سن درنظر گرفته شد يك خطا پرتاب كنم. نكته اي كه وجود دارد اين است كه مي خواهم همزمان اعلام كنم كه چه سني براي چه كسي در نظر گرفته شده است كه خطا توليد شده است.
    حالا در كلاس Person روي Property Age وقتي كاربر سني بيش از 100 يا كمتر از 0 را ست كند يك خطا از نوع InvalidAgeException‌ پرتاب خواهم كرد:
    [​IMG]

    در نتيجه وقتي به يك شيء از اين كلاس مقداري نامعتبر براي سن مشخص كنيم كاربر خطا دريافت خواهد كرد. نكته مهم اين است كه شما مي توانين اطلاعات فردي كه خطا بر روي آن اعلام شده و مقداري كه به عنوان سن براي او در نظر گرفته شده بود را هم داشته باشيد و نمايش دهيد.
     
  10. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : آموزش جامع تصویری و کاربردی سی شارپ (#c)

    کلاس های Abstract در سي شارپ

    قبلا در مورد Inheritance و ارث بری در سی شارپ صحبت کردم. گفتیم که در سی شارپ شما می توانید از یک کلاس به ارث برید و در صورت نیاز رفتارهای آن را override یا hide نمائید. فرض کنید که در طراحی نرم افزار پرسنلی شرکت دارید. در این سازمان دو نوع کارمند وجود دارد. کارمند ساعتی و کارمند حقوق بگیر ماهیانه. کارمندان را چگونه طراحی می کنید؟

    به نظر من می توانید یک کلاس به نام Employee ایجاد کنید و اطلاعات مشترک بین هر دو نوع کارمند را در این کلاس ایجاد کنین. من در این کلاس اطلاعات نام , نام خانوادگی, سن, کد کارمندی , حقوق و یک متد برای پرداخت حقوق ایجاد کردم. سپس دو کلاس MonthlyEmployee و HourlyEmployee را از کلاس Employee به ارث بردم. نکته ای که وجود دارد این است که در کلاس Employee به وجود Salary و PrintSalary نیاز دارم اما نمی دانم که برای موجودیتی به نام کارمند که در واقع وجود خارجی ندارد(چون در سازمان من همه کارمندان یا ساعتی هستند یا حقوق بگیر و شیء از جنس کارمند محض وجود ندارد) و اینکه من نمی دانم که حقوق این نوع کارمند چگونه پرداخت و محاسبه می شود و در واقع حقوق برای کارمند محض معنی ندارد و من صرفا این خاصیت و متد را برای override کردن در کلاس های فرزند ایجاد کرده ام.

    در چنین مواردی شما می توانید متد ها و property هایی که در کلاس پایه تان امکان پیاده سازیشان وجود ندارد را به صورت abstract تعریف کنین. در واقع با این کار به کامپایلر سی شارپ می فهمانید که این خاصیت یا متد صرفا به جهت override شدن در کلاس های فزرند ایجاد شده است. برای اینکه یک عضو abstract تعریف کنین باید کلمه abstract را در تعریف آن آورده و بدنه آن متد یا Property را حذف نمائید:

    [​IMG]
    دقت فرمائید که وقتی یک کلاس دارای عضو abstract باشد آن کلاس نیز باید abstract شود.

    [​IMG]

    خصوصیات کلاس های abstract:

    • وقتی کلاسی دارای یک عضو abstract باشد آن کلاس هم باید abstract شود.
    • وقتی یک کلاس به صورت abstract ایجاد شد از آن کلاس نمی توان شیء جدید ساخت.
    • وقتی از یک کلاس abstract به ارث می روید باید عضو های انتزاعی (abstract members) آن را override کنید.
    • کلمه abstract برای یک عضو (Member) کارایی کلمه virtual را نیز داراست.
    • وقتی از یک کلاس abstract به ارث می روید در صورتیکه حتی یکی از عضوهای abstract آن را override نکنید آن کلاس هم باید abstract شود.
    • اگر از یک کلاس که یک کلاس abstract را پیاده سازی کرده به ارث برویم می توانیم دوباره اعضاء abstract آن را override کنیم.
    • abstract کلاس ها می توانند دارای constructor باشند