زنگ سی شارپ – قسمت چهل و یکم

آشنایی با virtual method و method overriding و کلاس abstract و polymorphism


مسعود درویشیان 15 دیدگاه سی شارپ Friday, 13th September , 2013 43317 بازدید

در قسمت قبل اندکی با virtual method آشنا شدید. همان‌طور که ذکر شد، پروسه‌ی تعریف مجدد virtual method در derived class را method overriding می‌نامند.

زنگ سی‌شارپ - قسمت چهل و یکم

همان‌طور که گفته شد، virtual method در base class با کلمه‌ی‌کلیدی virtual تعریف می‌شود. هنگامی‌که یک virtual method در derived class مجدداً تعریف می‌شود، باید از override modifier استفاده ‌کنید و هنگام override کردن یک متد، باید اسم متد، return type و پارامترهای آن را مطابق با virtual method بنویسید.

به مثال زیر توجه کنید:

using System;
class Human
{
    public virtual void SayHello(string name)
    {
        Console.WriteLine("SayHello in base class");
    }
}
class Man : Human
{
    public override void SayHello(string name)
    {
        Console.WriteLine("Hello " + name);
    }
}
class OverrideDemo
{
    static void Main()
    {
        Man ob = new Man();
        ob.SayHello("Stefan");
    }
}

متد ()SayHello درکلاس Human به‌صورت virtual تعریف شده است و یک پارامتر دارد. در کلاس Man که از Human ارث‌بری کرده، متد مربوطه override شده است. همان‌طور که می‌بینید این متد در کلاس  Man از override modifier استفاده کرده است. دقت کنید که override کردن یک متد ضروری نیست و در صورتی‌که متدی را override نکنید، آن نسخه از متد که در base class وجود دارد اجرا خواهد شد.

به مثال زیر توجه کنید:

using System;
class A
{
    public virtual void SayHello()
    {
        Console.WriteLine("SayHello in base class");
    }
}
class B : A
{
    public override void SayHello()
    {
        Console.WriteLine("SayHello in B");
    }
}
class C : A
{ 
    // this class doesn't override SayHello()
}
class OverrideDemo
{
    static void Main()
    {
        A a = new A();
        B b = new B();
        C c = new C();

        a.SayHello();
        b.SayHello();
        c.SayHello();
    }
}

خروجی:

در این‌جا، کلاس C متد ()SayHello را override نمی‌کند بنابراین زمانی‌که متد ()SayHello از طریق شیء c فراخوانی می‌شود، متد ()SayHello در کلاس A اجرا خواهد شد.

هنگامی‌که از سلسله مراتب ارث‌بری استفاده می‌کنید، اگر یک derived class، یک virtual method را override نکند، به طرف ابتدای زنجیره‌ی ارث‌بری حرکت کنید، اولین override آن متد که دیده شود اجرا خواهد شد.

به مثال زیر توجه کنید:

using System;
class A
{
    public virtual void SayHello()
    {
        Console.WriteLine("SayHello in base class");
    }
}
class B : A
{
    public override void SayHello()
    {
        Console.WriteLine("SayHello in B");
    }
}
class C : B
{ 
    // this class doesn't override SayHello()
}
class D : C
{ 
    // this class doesn't override SayHello()
}
class OverrideDemo
{
    static void Main()
    {
        D d = new D();
        d.SayHello();
    }
}

خروجی:

همان‌طور که در مثال بالا می‌بینید، کلاس D از C و کلاس C از B و کلاس B از A ارث‌بری کرده است. کلاس D و C متد ()SayHello را override نکرده‌اند اما کلاس B این متد را override کرده است. بنابراین هنگامی که از طریق شیء کلاس D این متد را صدا می‌زنید، در زنجیره‌ی ارث‌بری اولین کلاسی که متد ()SayHello را فراخوانی کرده است کلاس B است. بنابراین همان‌طور که در خروجی می‌بینید، نسخه‌ی override شده‌ی این متد، موجود در کلاس B، اجرا خواهد شد. قابل ذکر است که properties و indexers نیز می‌توانند با استفاده از virtual و override به همین شکل مورد استفاده قرار گیرند.

علت استفاده از متدهای override شده چیست؟

متدهای override شده به سی‌شارپ اجازه می‌دهند تا از ویژگی runtime polymorphism بهره ببرد. Polymorphism توانایی ساخت متدهایی است که با توجه به موقعیت، می‌توانند اجرای متفاوتی داشته باشند. برای مثال شما می‌تواند هم به ماشین و هم به سگ غذا بدهید اما خوب می‌دانید که معنای غذا دادن به این‌دو کاملاً متفاوت است. Polymorphism به این دلیل برای برنامه‌نویسی شی‌گرا اهمیت دارد که به یک کلاس کلی، اجازه می‌دهد متدهایی داشته باشد که در همه‌ی کلاس‌های مشتق شده از آن کلاس، مشترک هستند. این درحالی است که به derived class ها این اجازه را می‌دهد تا هرطور که می‌خواهند آن متدها را اجرا کنند و درصورت نیاز، نحوه‌ی اجرای آن متدها را تغییر دهند. متدهای override شده، روش دیگری برای اجرای این جنبه از polymorphism که می‌گوید “one interface, multiple methods” هستند.

به مثال زیر توجه کنید:

// Use virtual methods and polymorphism.
using System;
class TwoDShape
{
    double pri_width;
    double pri_height;

    // A default constructor.
    public TwoDShape()
    {
        Width = Height = 0.0;
        name = "null";
    }

    // Parameterized constructor.
    public TwoDShape(double w, double h, string n)
    {
        Width = w;
        Height = h;
        name = n;
    }

    // Construct object with equal width and height.
    public TwoDShape(double x, string n)
    {
        Width = Height = x;
        name = n;
    }

    // Construct a copy of a TwoDShape object.
    public TwoDShape(TwoDShape ob)
    {
        Width = ob.Width;
        Height = ob.Height;
        name = ob.name;
    }

    // Properties for Width and Height.
    public double Width
    {
        get { return pri_width; }
        set { pri_width = value < 0 ? -value : value; }
    }
    public double Height
    {
        get { return pri_height; }
        set { pri_height = value < 0 ? -value : value; }
    }

    public string name { get; set; }

    public void ShowDim()
    {
        Console.WriteLine("Width and height are " +
        Width + " and " + Height);
    }
    public virtual double Area()
    {
        Console.WriteLine("Area() must be overridden");
        return 0.0;
    }
}

// A derived class of TwoDShape for triangles.
class Triangle : TwoDShape
{
    string Style;

    // A default constructor.
    public Triangle()
    {
        Style = "null";
    }

    // Constructor for Triangle.
    public Triangle(string s, double w, double h) :
        base(w, h, "triangle")
    {
        Style = s;
    }

    // Construct an isosceles triangle.
    public Triangle(double x)
        : base(x, "triangle")
    {
        Style = "isosceles";
    }

    // Construct a copy of a Triangle object.
    public Triangle(Triangle ob)
        : base(ob)
    {
        Style = ob.Style;
    }

    // Override Area() for Triangle.
    public override double Area()
    {
        return Width * Height / 2;
    }

    // Display a triangle's style.
    public void ShowStyle()
    {
        Console.WriteLine("Triangle is " + Style);
    }
}

// A derived class of TwoDShape for rectangles.
class Rectangle : TwoDShape
{
    // Constructor for Rectangle.
    public Rectangle(double w, double h) :
        base(w, h, "rectangle") { }

    // Construct a square.
    public Rectangle(double x) :
        base(x, "rectangle") { }

    // Construct a copy of a Rectangle object.
    public Rectangle(Rectangle ob) : base(ob) { }

    // Return true if the rectangle is square.
    public bool IsSquare()
    {
        if (Width == Height) return true;
        return false;
    }

    // Override Area() for Rectangle.
    public override double Area()
    {
        return Width * Height;
    }
}
class DynShapes
{
    static void Main()
    {
        TwoDShape[] shapes = new TwoDShape[5];

        shapes[0] = new Triangle("right", 8.0, 12.0);
        shapes[1] = new Rectangle(10);
        shapes[2] = new Rectangle(10, 4);
        shapes[3] = new Triangle(7.0);
        shapes[4] = new TwoDShape(10, 20, "generic");

        for (int i = 0; i < shapes.Length; i++)
        {
            Console.WriteLine("object is " + shapes[i].name);
            Console.WriteLine("Area is " + shapes[i].Area());

            Console.WriteLine();
        }
    }
}

خروجی:

در برنامه‌ی بالا، ابتدا ()Area به‌صورت virtual در کلاس TwoDShape تعریف شده و سپس توسط کلاس‌های Triangle و Rectangle نیز override شده است. در TwoDShape می‌بینید که ()Area فقط به‌صورت virtual تعریف شده است و تنها کاری که انجام می‌دهد این است که اطلاع می‌دهد این متد باید override شود. هر override از متد ()Area باید بستگی به شکل شیء‌ای داشته باشد که derived class نشان دهنده‌ی آن است. به‌عنوان مثال اگر شکل مورد نظر مستطیل است نحوه‌ی محاسبه‌ی مساحت آن متناسب با مستطیل خواهد بود و اگر شکل مورد نظر مثلث باشد، نحوه‌ی محاسبه‌ی مساحت آن نیز متناسب با مثلث است. نکته‌ی مهم دیگر برنامه‌ی بالا درون متد ()Main است. همان‌طور که می‌بینید shapes آرایه‌ای از اشیای TwoDShape است اما عناصری که در این آرایه قرار دادیم reference های Triangle و Rectangle و TwoDShape هستند. همان‌طور که قبلاً ذکر شد، این مورد به این دلیل صحیح است که base class reference می‌تواند به derived class object رجوع کند. این برنامه سپس توسط یک حلقه، اطلاعات عناصر موجود در آرایه را نمایش می‌دهد.

استفاده از کلاس‌های Abstract

گاهی قصد دارید یک base class بسازید که تنها یک فرم کلی را مشخص می‌کند و آن را با تمام کلاس‌های مشتق شده، به اشتراک می‌گذارد و اجازه می‌دهد که خود derived class ها بدنه‌ و جزئیات این فرم کلی را تکمیل کنند. به‌عنوان مثال، این چنین کلاسی ماهیت یک متد را مشخص می‌کند و derived class ها باید این متد را override کنند اما خود base class دیگر نیازی ندارد که برای این متد یک اجرای پیش‌فرض داشته باشد. این حالت ممکن است زمانی رخ دهد که base class نتواند یک اجرای بامعنی برای متد مورد نظر داشته باشد، از این‌رو اجرا را بر عهده‌ی derived class ها می‌گذارد. مانند مثال قبل که متد ()Area در کلاس TwoDShape، هیچ‌گونه محاسباتی را انجام نمی‌داد. در چنین مواقعی، می‌توانید مانند مثال قبل به‌سادگی یک پیغام هشدار درون متد قرار دهید اما این روش چندان مناسب نیست و ممکن است در شرایط خاصی مثل debug کردن، مناسب باشد. گاهی ممکن است متدهایی در base class داشته باشید که derived class ها حتماً باید آن‌ها را اجرا کنند، در چنین شرایطی باید از abstract method استفاده کنید.

یک متد abstract با abstract modifier ساخته می‌شود. abstract method بدنه ندارد و از این‌رو درون base class اجرا نخواهد شد. derived class ها حتماً باید این abstract method را override کنند. یک abstract method به‌صورت اتوماتیک virtual نیز است و در واقع نمی‌توانید از virtual و abstract باهم در یک تعریف استفاده کنید.

فرم کلی abstract method به‌شکل زیر است:

abstract type name(parameter-list);

همان‌طور که می‌بینید، در abstract method به بدنه نیاز ندارید. دقت کنید که abstract modifier را نمی‌توانید برای متدهای static استفاده کنید. properties و indexers نیز می‌توانند abstract  باشند.

کلاسی که شامل یک یا بیشتر از یک متد abstract باشد باید به‌صورت abstract تعریف شود. برای تعریف یک کلاس به‌صورت abstract کافی است که قبل از کلمه‌ی کلیدی class از abstract modifier استفاده کنید. از آن‌جا که abstract class نمی‌تواند به‌طور کامل اجرا شود (به‌دلیل وجود متدهای abstract که بدنه ندارند)، به‌همین دلیل نمی‌توانید از abstract class شیء بسازید.

هنگامی‌که یک derived class از یک abstract class ارث‌بری می‌کند باید تمام متدهای abstract در base class را override کند در غیر این‌صورت derived class نیز باید به‌صورت abstract تعریف شود.

به مثال زیر توجه کنید:

using System;
abstract class TwoDShape
{
    double pri_width;
    double pri_height;

    // Parameterized constructor.
    public TwoDShape(double w, double h, string n)
    {
        Width = w;
        Height = h;
        name = n;
    }

    // Properties for Width and Height.
    public double Width
    {
        get { return pri_width; }
        set { pri_width = value < 0 ? -value : value; }
    }
    public double Height
    {
        get { return pri_height; }
        set { pri_height = value < 0 ? -value : value; }
    }
    public string name { get; set; }

    // Now, Area() is abstract.
    public abstract double Area();
}

// A derived class of TwoDShape for triangles.
class Triangle : TwoDShape
{
    string Style;

    // Constructor for Triangle.
    public Triangle(string s, double w, double h)
        : base(w, h, "triangle")
    {
        Style = s;
    }

    // Override Area() for Triangle.
    public override double Area()
    {
        return Width * Height / 2;
    }
}

// A derived class of TwoDShape for rectangles.
class Rectangle : TwoDShape
{
    // Constructor for Rectangle.
    public Rectangle(double w, double h) :
        base(w, h, "rectangle") { }

    // Override Area() for Rectangle.
    public override double Area()
    {
        return Width * Height;
    }
}

class AbsShape
{
    static void Main()
    {
        Triangle triangle = new Triangle("right", 8.0, 12.0);
        Rectangle rectangle = new Rectangle(10, 4);

        Console.WriteLine("Triangle | Area: " + triangle.Area());
        Console.WriteLine("Rectangle | Area: " + rectangle.Area());
    }
}

همان‌طور که برنامه نشان می‌دهد، همه‌ی derived class ها بایستی ()Area را override کنند (یا اینکه خودشان باید abstract باشند). نکته‌ی دیگر این است که یک abstract class می‌تواند متدهایی داشته باشد که abstract نیستند و derived class ها می‌توانند در صورت نیاز آن‌ها را override کنند درحالی که هیچ اجباری در کار نیست.



نویسنده / مترجم : مسعود درویشیان

علاقه مند به موسیقی و برنامه نویسی بازی


15 دیدگاه برای این نوشته ثبت شده است


  1. رها
    19 September 2013

    سلام و عرض ادب حضور اقای درویشیان
    و تشکر بخاطر این موارد فوق العاده
    اقای درویشیان من از اموزش #C هدفم بیشتر طراحی وب بود وقتی این سری اموزشی رو روع کردم اما مثل اینکه این مبحث اموزشی اصلا به وب و طراحی سایت مربوط نمیشه
    من برا ساس انچه که در چند اله گذشته اموخته بودم (html , css ) میخواستم ادامه اون رو با asp.net پیش ببرم
    حالا خواهشم اینه اگر برای شما مقدوره و در این زمینه اموزش خوبی رو یا وب سایت اموزشی (البته بجز اموزشها استاد کیانیان و اموزشهای انیاک ) اموزش کامل و جامعی برای طراحی وب سایت و قالب وب سایتها با استفاده ترکیبی از کدهای html ,css با ASP.NET در زمینه کاری میشناسید لطفا معرفی کنید واقعا ممنون میشم
    من هرچقدر سرچ زدم اکثر اموزشها برای سطوح بالا بوده و برای مبتدیان در بین اونها چیزی پیدا نکردم
    واقعا ممنون میشم اگر با مهارتی که دراین زمینه دارید منو برای استارت شروع در این زمینه راهنمایی کنید
    با تشکر وارزوی توفیق ورزافزون
    شاد باشید



    • اگر قصد دارید asp.net کار کنید که حتماً باید #C یا VB.Net رو مسلط باشید و به همین دلیل خوندن مقالات زنگ سی‌شارپ کمک زیادی بهتون می‌کنه
      مقاله فارسی خاصی تو این زمینه سراغ ندارم و پیشنهادم اینه که کتاب‌های انگلیسی رو مطالعه بفرمایید. توی سایت http://www.asp.net هم مطالب خیلی مقیدی می‌تونید پیدا کنید. کتاب‌های پیشنهادی توی سایت asp.net عالی هستن




      • رها
        23 September 2013

        سلام
        خیلی ممنون و تشکر بخاطر معرفی مقاله و وب سایت ASP.NET
        موفق باشید




  2. تکران سرویس
    23 September 2013

    سلام..ممنون به خاطر این سری از اموزش ها.. لطفا ادامه بدیدو




  3. سروش
    24 September 2013

    سلام
    خسته نباشید
    به سفارش دوستان به این صفحه اومدم و با اینکه رشته ام نیست ولی از اموزشها واقعا لذت بردم خیلی کامل توضیح داده شده
    در صورتی که مقدوره و در توان شما هست خواهش میکنم در کنار این سری اموزشی آموزش ساخت وب اپلیکیشن (MVC) رو هم در کنارش شروع کنید و حداقل یه پروژه خیلی مختصر در مورد طراحی وب با این زبان رو بصورتی که الان دارید #C رو اموزش میدین بذارید
    اموزشهای شما خیلی روان توضیح داده شده تا بحال اموزشی رو در این مقوله تا این حد کامل ندیده بودم که تمام نکات و خط به خط کدها رو کامل نوشته باشه
    واقعا ممنون میشم اگر برای کسانی که بدنبال طراحی وب در قالب پروژه mvc هستند هم یه بخش اموزشی رو راه اندازی کنید
    با ارزوی توفیق و راه اندازی این بخش



  4. بسیار عالی خسته نباشید




  5. رها
    23 October 2013

    سلام و عرض ادب
    امیدوارم با سوالات و درخواستهام شما رو خسته نکرده باشم
    اما من بعد از توصیه شما به فراگیری #C برای اینکه بتونم در نهایت با ASP.NET کار کنم شروع بکار کردم
    اموزشها شما رو وقتی تمرین میکنم بیشتر در صفحه داس اجرا میشه اما ASP.NET در مرورگر اجرا میشه
    امیدکه بتونم منظورم رو بیان کنم . من در ASP همونطور که گفتم تازه کارم و وفقط با html , css اشنایی دارم
    حالا نمیدونم یادگیری #C میتونه در نهایت برای طراحی سایت با ASP کمکی بکنه
    اصلا کار سی شارپ و نقش اون در ASP چی هست
    در صورت امکان و اگر فرصتی بود لطفا راهنمایی کنید که ایا با دنبال کردن این اموزش میشه بعدا از این کدها در ASP هم استفاده کرد ؟
    با تقدیر وتشکر و ارزوی توفیق



    • سلام. شما برای استفاده از asp.net نیاز دارید که سی‌شارپ (یا VB.net) رو یاد بگیرید چون زبان برنامه‌نویسی‌ای که توی asp.net استفاده می‌شه، #C هست. برای این مجموعه آموزشی تصمیم بر این بود که سی‌شارپ رو روی کنسول آموزش بدیم تا یادگیری خیلی ساده‌تر بشه. شما از طریق این مجموعه زبان سی‌شارپ رو یاد میگیرید و می‌تونید در برنامه‌نویسی وب، موبایل، دسکتاپ، ساخت بازی و حتی سخت‌افزار از سی‌شارپ استفاده کنید. در نهایت زبان سی‌شارپ (چیزی که توی این مجموعه آموزشی یاد می‌گیرید) توی محیط‌های مختلف عوض نمی‌شه فقط باید با نحوه‌ی برنامه‌نویسی روی هر محیط آشنا بشید. مثلاً وب و موبایل و دسکتاپ هرکدوم تکنیک برنامه‌نویسی خاص خودشون رو دارند اما می‌تونید برای همشون از سی‌شارپ استفاده کنید.

      در نهایت اگر می‌خواهید روی وب کار کنید با خوندن کتاب‌ها و مقالات مربوط به asp.net یاد می‌گیرید که چطوری از سی‌شارپ توی وب استفاده می‌شه.




  6. حمیدرضا
    31 December 2013

    آقا مسعود سلام

    یه نظر من override کردن و نکردن یک متد هیچ فرقی نمیکند مثالی که شما بالا زدید با این نمونه چه فرقی می کند؟

    using System;
    class Human
    {
        public void SayHello(string name)
        {
            Console.WriteLine("SayHello in base class");
        }
    }
    class Man : Human
    {
        public void SayHello(string name)
        {
            Console.WriteLine("Hello " + name);
        }
    }
    class OverrideDemo
    {
        static void Main()
        {
            Man ob = new Man();
            ob.SayHello("Stefan");
        }
    }
    

    شما تو توضیحات گفتید که اگر از override و virtual استفاده نکنیم و در صورتی‌که متدی را override نکنید، آن نسخه از متد که در base class وجود دارد اجرا خواهد شد.ولی همانطور که بالا میبینید من نه از virtual و نه از override استفاده کردم و با اجرای این برنامه هیچ تفاوتی ندارد.
    این طور که من متوجه شدم این مورد تنها در کلاسهایی کاربرد دارد که هیچ متدی در آن تعریف نشده است.
    اگر امکان دارد بیشتر توضیح دهید.



    • سلام. دوست عزیز اگه توجه کنی وقتی این برنامه رو می‌خوای compile کنی یه warning بهت می‌ده که این هست:
      Warning 1 ‘Man.SayHello(string)’ hides inherited member ‘Human.SayHello(string)’. Use the new keyword if hiding was intended
      این یعنی اینکه متد SayHello شما توی کلاس Man دیگه اجازه نمی‌ده شما مستقیماً از طریق اشیای این کلاس به متد SayHello کلاس Human دسترسی داشته باشی. چرا؟ چون دقیقاً هم‌نام هستند و پارامترهاشون هم یکیه! پس وقتی شما از طریق شیء Man متد SayHello رو صدا می‌زنی، چون اسم متد با متد کلاس پدرش یکی هست باعث می‌شه کامپایلر یه هشدار بده که دیگه شما مستقیماً به متد کلاس پدر دسترسی نداری! به همین خاطر هم موقع اجرا، متد موجود توی کلاس Man اجرا می‌شه. کلاً فلسفه‌ی virtual و override این هست که شما بدونی یه متد ممکنه کامل نباشه و فقط بخشی از کارهارو انجام بده و شما این اجازه رو داری که واسه کلاس خودت کاملش کنی یا این‌که کاملاً تغییرش بدی
      یه بار دیگه مطالب عنوان “علت استفاده از متدهای override شده چیست” رو مرور کنی بهتر متوجه می‌شی
      اگه بازم سوالی بود حتما بپرسید




      • حمیدرضا
        31 December 2013

        ممنون از پاسختون ولی من این جا رو متوجه نشدم که شما مستقیماً از طریق اشیای این کلاس به متد SayHello کلاس Human دسترسی داشته باشی. من مثال دوم شما رو که در مورد مستطیل و … هست رو overrid و virtual هایش رو برداشتم و تفاوت رو حس کردم ولی چون برنامه تعداد خطوط زیاد داشت متوجه نشدم که چه اتفاقی افتاد. در مثالی که براتون نوشتم برداشتم ولی هیچ اتفاقی نیفتاد. الان مشکل من تو همینه که چرا تو یکیش فرق داشت تو اون یکی نداشت.
        بازم ممنون از جوابتون




  7. elina
    23 January 2015

    سلام جناب آقای درویشیان

    من قبلا شنیدم بودم برای یادگیری asp.net نیاز به یادگیری c# نیست.
    بخاطر همین پروژه رو asp.net انتخاب کردم.
    ساخت فروشگاه انلاین هست.

    خواستم بپرسم، الان بدبخت شدم؟



    • البته که نیازه، هرچند با ویژوال بیسیک هم می‌شه برنامه‌نویسی‌اش رو انجام داد. با این حال، باید سی‌شارپ رو یاد بگیرید. یا اینکه کلاً پروژه‌تون رو با php انجام بدید.




  8. فرزانه
    6 November 2015

    وافعا عااالی بود




  9. ابراهیم
    19 November 2015

دیدگاه خود را بنویسید





نشانی ایمیل شما منتشر نخواهد شد.

کامنت های شما بعد از تأیید توسط نویسنده وبلاگ، منتشر خواهند شد.

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

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

برای قرار دادن کدهای نمونه می توانید از تگ های [php] ، [html] ، [css] و [js] استفاده کنید.
به عنوان مثال کدهای php را می توان به صورت زیر قرار داد:
[php] var $whoLoveIranians = "WebTarget!"; [/php]



کلیه حقوق مادی و معنوی برای وب سایت وب تارگت محفوظ است ©2024 وب‌تارگت

استفاده از مطالب وب سایت در سایر وب سایت‌ها و نشریات چاپی با ذکر منبع آزاد است.