Skip to content

3. کلاس‌ها، ساختارها، رابط‌ها

3.1. ابجه‌ها به عنوان مثال

3.1.1. عمومی

اکنون برنامه‌نویسی شیءگرا را از طریق مثال‌ها بررسی خواهیم کرد. یک شیء یک موجودیت است که شامل داده‌هایی است که وضعیت آن را تعریف می‌کنند (که به آن‌ها ویژگی‌ها گفته می‌شود) و توابع (که به آن‌ها متدها گفته می‌شود). یک شیء بر اساس قالبی به نام کلاس ایجاد می‌شود:

Public Class c1
     ' ویژگی‌ها
    Private p1 As type1
    Private p2 As type2
....

     ' روش
    Public Sub m1(....)
...
    End Sub    

     ' روش
    Public Function m2(...)
....
    End Function    
End Class

از کلاس قبلی C1، می‌توان اشیاء متعددی مانند O1، O2، … ایجاد کرد. همهٔ این‌ها دارای خواص p1، p2 و ... و متدهای m3، m4 و ... خواهند بود. با این حال، مقادیر خواص آن‌ها متفاوت است، به این معنی که هر pi وضعیت متمایز خود را دارد. به طور مشابه، اعلان

    dim i, j as integer

دو شیء (این اصطلاح در اینجا نادرست است) از نوع (کلاس) Integer ایجاد می‌کند. تنها ویژگی آنها مقدارشان است. اگر O1 یک شیء از نوع C1 باشد، O1.p1 به خصوصیت p1 از O1 اشاره می‌کند، و O1.m1 به متد m1 از QZXW2HTMLP0003 اشاره می‌کند.06ZQX. بیایید یک مدل شیء اولیه را در نظر بگیریم: کلاس personne.

3.1.2. تعریف کلاس Person

تعریف کلاس personne به شرح زیر خواهد بود:


Public Class personne
    ' ویژگی‌ها
    Private prenom As String
    Private nom As String
    Private age As Integer

    ' روش
    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub

    ' روش
    Public Sub identifie()
        Console.Out.WriteLine((prenom & "," & nom & "," & age))
    End Sub
End Class

در اینجا ما تعریف یک کلاس و در نتیجه یک نوع داده را داریم. هنگامی که متغیرهایی از این نوع ایجاد می‌کنیم، به آنها اشیاء یا نمونه‌های کلاس می‌گوییم. بنابراین یک کلاس یک قالب است که اشیاء از روی آن ساخته می‌شوند. اعضای یا فیلدهای یک کلاس می‌توانند داده (ویژگی‌ها)، متدها (تابع‌ها) یا خواص باشند. خواص، متدهای خاصی هستند که برای بازیابی یا تنظیم مقدار ویژگی‌های یک شیء استفاده می‌شوند. این فیلدها ممکن است با یکی از سه کلمه کلیدی زیر همراه باشند:

privé
یک فیلد خصوصی تنها توسط متدهای داخلی کلاس قابل دسترسی است
public
یک فیلد عمومی برای هر تابع قابل دسترسی است، چه در داخل کلاس تعریف شده باشد چه نباشد
protégé
یک فیلد محافظت‌شده تنها از طریق متدهای داخلی کلاس یا توسط یک شیء مشتق‌شده قابل دسترسی است (مفهوم ارث‌بری را بعداً ببینید).

به‌طور کلی، داده‌های یک کلاس به‌صورت خصوصی اعلام می‌شوند، در حالی که متدها و ویژگی‌های آن به‌صورت عمومی اعلام می‌شوند. این بدان معناست که کاربر یک شیء (برنامه‌نویس):

  • دسترسی مستقیم به داده‌های خصوصی شیء نخواهد داشت
  • می‌تواند متدهای عمومی شیء را فراخوانی کند، به‌ویژه آنهایی که دسترسی به داده‌های خصوصی آن را فراهم می‌کنند.

سینتکس تعریف یک کلاس به شرح زیر است:


public class classe
    private  donnée ou méthode ou propriété privée
    public  donnée ou méthode ou propriété publique
    protected  donnée ou méthode ou propriété protégée
end class

ترتیب اعلام خصوصیات private، protected و public دلخواه است.

3.1.3. متد مقداردهی اولیه می‌کند

بیایید به کلاس [personne] خود بازگردیم که به صورت زیر تعریف شده است:


Public Class personne
    ' ویژگی‌ها
    Private prenom As String
    Private nom As String
    Private age As Integer

    ' روش
    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub

    ' روش
    Public Sub identifie()
        Console.Out.WriteLine((prenom & "," & nom & "," & age))
    End Sub
End Class

نقش متد initialise چیست؟ از آنجا که nom، prenom و age اعضای خصوصی کلاس personne هستند، دستورات:

dim p1 as personne p1
p1.prenom="Jean"
p1.nom="Dupont"
p1.age=30

نامعتبر هستند. ما باید یک شیء از نوع personne را از طریق یک متد عمومی مقداردهی اولیه کنیم. این وظیفه متد initialise است. خواهیم نوشت:

dim p1 as personne
p1.initialise("Jean","Dupont",30)

نوتاسیون p1.initialise معتبر است زیرا initialise به‌صورت عمومی قابل دسترسی است.

3.1.4. اپراتور جدید

ترتیب دستورالعمل‌ها

dim p1 as personne
p1.initialise("Jean","Dupont",30)

نادرست است. دستور

dim p1 as personne

p1 را به‌عنوان مرجعی به یک شیء از نوع personne اعلام می‌کند. این شیء هنوز وجود ندارد و بنابراین p1 مقداردهی اولیه نمی‌شود. گویی که کسی بنویسد:

dim p1 as personne=nothing

که در آن کلمه کلیدی nothing صراحتاً نشان می‌دهد که متغیر p1 هنوز به هیچ شیئی ارجاع نمی‌دهد. وقتی سپس می‌نویسیم

p1.initialise("Jean","Dupont",30)

ما در حال فراخوانی متد initialise از شی‌ای هستیم که توسط p1 ارجاع شده است. با این حال، این شی هنوز وجود ندارد و کامپایلر خطا گزارش خواهد کرد. برای اینکه p1 به یک شی ارجاع دهد، باید بنویسیم:

dim p1 as personne=new personne()

این کار یک شیء از نوع personne ایجاد می‌کند که هنوز مقداردهی اولیه نشده است: ویژگی‌های nom و prenom، که ارجاع به اشیایی از نوع String هستند، مقدار nothing را خواهند داشت، و age مقدار 0 را خواهد داشت. بنابراین یک مقداردهی پیش‌فرض وجود دارد. اکنون که p1 به یک شیء ارجاع می‌دهد، عبارت مقداردهی اولیه برای آن شیء

p1.initialise("Jean","Dupont",30)

صحیح است.

3.1.5. کلمه کلیدی Me

بیایید کد متد initialise را بررسی کنیم:


    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub    

دستور Me.prenom=P به این معناست که ویژگی prenom از شیء فعلی (Me) مقدار P را دریافت می‌کند. کلمه کلیدی Me به شیء فعلی اشاره دارد: شیئی که متدی که در حال اجراست در آن قرار دارد. چگونه این را می‌دانیم؟ بیایید ببینیم که شیء مرجع p1 در برنامه فراخوانی‌کننده چگونه مقداردهی اولیه می‌شود:

p1.initialise("Jean","Dupont",30)

این متد initialise از شیء p1 است که فراخوانی می‌شود. وقتی در این متد به شیء Me ارجاع داده می‌شود، در واقع به شیء p1 ارجاع داده می‌شود. روش initialise می‌توانست به صورت زیر نیز نوشته شود:


    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        prenom = P
        nom = N
        Me.age = age
    End Sub    

وقتی یک متد یک شی به یک ویژگی A از آن شی ارجاع می‌دهد، نشانه Me.A به‌طور ضمنی در نظر گرفته می‌شود. در مواردی که بین شناسه‌ها تداخل وجود دارد، باید به‌طور صریح از آن استفاده شود. این مورد در عبارت زیر صادق است:


Me.age=age;

که در آن age به یک ویژگی از شیء جاری و همچنین پارامتر age دریافتی توسط متد اشاره می‌کند. آنگاه باید این ابهام با ارجاع به صفت age به‌عنوان Me.age برطرف شود.

3.1.6. یک برنامهٔ آزمایشی

در اینجا یک برنامه آزمایشی کوتاه آمده است:


Public Class personne
    ' ویژگی‌ها
    Private prenom As String
    Private nom As String
    Private age As Integer

    ' روش
    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub

    ' روش
    Public Sub identifie()
        Console.Out.WriteLine((prenom & "," & nom & "," & age))
    End Sub
End Class

و نتایج به‌دست‌آمده:

dos>vbc personne1.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>personne1
Jean,Dupont,30

3.1.7. استفاده از یک فایل کلاس کامپایل‌شده (اسمبلی)

توجه کنید که در مثال قبلی، دو کلاس در برنامه آزمایشی ما وجود دارد: کلاس‌های personne و test1. راه دیگری برای انجام این کار وجود دارد:

  • کلاس «person» را در یک فایل جداگانه به نام assembly کامپایل می‌کنیم. این فایل دارای پسوند .dll است.

  • ما کلاس **test1** را با ارجاع به اسمبلی حاوی کلاس person کامپایل می‌کنیم.

دو فایل منبع به شکل زیر درمی‌آیند:

test.vb

Module test1
    Sub Main()
        Dim p1 As New personne
        p1.initialise("Jean", "Dupont", 30)
        p1.identifie()
    End Sub
End module
personne2.vb

' گزینه‌ها
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

Public Class personne
    ' ویژگی‌ها
    Private prenom As String
    Private nom As String
    Private age As Integer

    ' روش
    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub    'initialise

    ' روش
    Public Sub identifie()
        Console.Out.WriteLine((prenom & "," & nom & "," & age))
    End Sub    'identifie
End Class 'personne

کلاس personne توسط عبارت زیر کامپایل می‌شود:

dos>vbc /t:library personne2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573

dos>dir
24/02/2004  16:50                  509 personne2.vb
24/02/2004  16:49                  143 test.vb
24/02/2004  16:50                3 584 personne2.dll

مجموعه‌سازی فایلی به نام personne2.dll تولید کرد. این گزینهٔ مجموعه‌سازی /t:library است که تولید یک فایل «اسمبلی» را مشخص می‌کند. اکنون بیایید فایل test.vb را مجموعه‌سازی کنیم:

dos>vbc /r:personne2.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
24/02/2004  16:50                  509 personne2.vb
24/02/2004  16:49                  143 test.vb
24/02/2004  16:50                3 584 personne2.dll
24/02/2004  16:51                3 072 test.exe

گزینهٔ کامپایل /r:personne2.dll به کامپایلر می‌گوید که کلاس‌های خاصی را در فایل personne2.dll پیدا خواهد کرد. وقتی در فایل منبع test.vb، ارجاعی به کلاس personne – کلاسی که در فایل منبع test.vb تعریف نشده است – پیدا می‌کند، این کلاس را در فایل‌های .dll که توسط گزینه /r ارجاع شده‌اند، جستجو خواهد کرد. در اینجا، کلاس personne را در اسمبلی personne2.dll پیدا خواهد کرد. می‌توانست کلاس‌های دیگری نیز در این اسمبلی گنجانده شوند. برای استفاده از چندین فایل کلاس کامپایل‌شده در طول کامپایل، باید می‌نوشت:

vbc /r:fic1.dll /r:fic2.dll ... fichierSource.vb

اجرای برنامه test1.exe نتایج زیر را تولید می‌کند:

dos>test
Jean,Dupont,30

3.1.8. یک متد دیگر مقداردهی اولیه شده است

بیایید دوباره کلاس personne را در نظر بگیریم و متد زیر را به آن اضافه کنیم:


    ' روش
    Public Sub initialise(ByVal P As personne)
        prenom = P.prenom
        nom = P.nom
        Me.age = P.age
    End Sub

اکنون ما دو متد با نام initialise داریم: این امر تا زمانی که پارامترهای متفاوتی را بپذیرند مجاز است. در اینجا نیز همین‌طور است. پارامتر اکنون یک مرجع (P) به یک شخص است. ویژگی‌های شخص P سپس به شیء فعلی (Me) تخصیص داده می‌شوند. توجه داشته باشید که متد initialise دسترسی مستقیم به ویژگی‌های شیء P دارد، هرچند این ویژگی‌ها از نوع private هستند. این امر همیشه صادق است: یک شیء O1 از کلاس C همیشه به ویژگی‌های اشیاء از همان کلاس، C، دسترسی دارد. در اینجا آزمایش کلاس جدید personne ارائه شده است که همان‌طور که قبلاً توضیح داده شد، به personne.dll کامپایل شده است:


' گزینه‌ها
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

Module test1
    Sub Main()
        Dim p1 As New personne
        p1.initialise("Jean", "Dupont", 30)
        Console.Out.Write("p1=")
        p1.identifie()
        Dim p2 As New personne
        p2.initialise(p1)
        Console.Out.Write("p2=")
        p2.identifie()
    End Sub
End Module

و نتایج آن:

p1=Jean,Dupont,30
p2=Jean,Dupont,30

3.1.9. سازنده‌های کلاس Person

یک سازنده، روندی به نام New است که هنگام ایجاد شی فراخوانی می‌شود. این معمولاً برای مقداردهی اولیه شی استفاده می‌شود. اگر یک کلاس دارای سازنده‌ای باشد که n آرگومان را می‌پذیرد argi، می‌توان یک شی از آن کلاس را به صورت زیر اعلام و مقداردهی اولیه کرد:


        dim objet as classe =new classe(arg1,arg2, ... argn)

یا


        dim objet as classe

        objet=new classe(arg1,arg2, ... argn)

وقتی یک کلاس یک یا چند سازنده دارد، باید از یکی از این سازنده‌ها برای ایجاد یک شیء از آن کلاس استفاده شود. اگر یک کلاس C هیچ سازنده‌ای نداشته باشد، یک سازندهٔ پیش‌فرض دارد که همان سازندهٔ بدون‌پارامتر است: public New(). سپس ویژگی‌های شیء با مقادیر پیش‌فرض مقداردهی می‌شوند. این همان چیزی است که در برنامه‌های قبلی رخ داد، جایی که ما نوشته بودیم:

    dim p1 as personne
    p1=new personne

بیایید دو سازنده برای کلاس personne خود ایجاد کنیم:


' گزینه‌ها
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

' کلاس Person
Public Class personne
    ' ویژگی‌ها
    Private prenom As String
    Private nom As String
    Private age As Integer

    ' سازنده‌ها
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        initialise(P, N, age)
    End Sub

    Public Sub New(ByVal P As personne)
        initialise(P)
    End Sub

    ' روش‌های инициализация شیء
    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub

    Public Sub initialise(ByVal P As personne)
        prenom = P.prenom
        nom = P.nom
        Me.age = P.age
    End Sub

    ' متد
    Public Sub identifie()
        Console.Out.WriteLine((prenom & "," & nom & "," & age))
    End Sub
End Class

هر دو کانستراکتور ما به سادگی متدهای متناظر initialise را فراخوانی می‌کنند. به یاد داشته باشید که وقتی در داخل یک کانستراکتور، به عنوان مثال، با notation initialise(P) مواجه می‌شویم، کامپایلر این را به Me.initialise(P) تبدیل می‌کند. بنابراین در داخل سازنده، متد initialise برای عمل بر روی ابجکتی که توسط Me ارجاع شده است، یعنی همان ابجکت فعلی که در حال ساخت است، فراخوانی می‌شود. در اینجا یک برنامه آزمایشی کوتاه آورده شده است:


' گزینه‌ها 
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

' صفحه آزمایشی
Module test
    Sub Main()
        Dim p1 As New personne("Jean", "Dupont", 30)
        Console.Out.Write("p1=")
        p1.identifie()
        Dim p2 As New personne(p1)
        Console.Out.Write("p2=")
        p2.identifie()
    End Sub
End Module

و نتایج به‌دست‌آمده:

p1=Jean,Dupont,30
p2=Jean,Dupont,30

3.1.10. اشاره‌های شیء

ما همیشه از همان کلاس personne استفاده می‌کنیم. برنامهٔ آزمایشی به شرح زیر است:


' گزینه‌ها
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

' صفحه آزمایشی
Module test
    Sub Main()
        ' p1
        Dim p1 As New personne("Jean", "Dupont", 30)
        Console.Out.Write("p1=")
        p1.identifie()

        ' p2 به همان شیء p1 اشاره می‌کند
        Dim p2 As personne = p1
        Console.Out.Write("p2=")
        p2.identifie()

        ' p3 به شی‌ای اشاره می‌کند که کپی‌ای از شی مورد اشارهٔ p1 خواهد بود
        Dim p3 As New personne(p1)
        Console.Out.Write("p3=")
        p3.identifie()

        ' وضعیت شیء مرجع p1 تغییر می‌کند
        p1.initialise("Micheline", "Benoît", 67)
        Console.Out.Write("p1=")
        p1.identifie()

        ' از آنجا که p2 = p1، حالت شی ارجاع‌شده توسط p2 باید تغییر کرده باشد
        Console.Out.Write("p2=")
        p2.identifie()

        ' از آنجا که p3 به همان شیء p1 اشاره نمی‌کند، شیء مورد اشاره p3 نباید تغییر کرده باشد
        Console.Out.Write("p3=")
        p3.identifie()
    End Sub
End Module

نتایج به‌دست‌آمده به شرح زیر است:

p1=Jean,Dupont,30
p2=Jean,Dupont,30
p3=Jean,Dupont,30
p1=Micheline,Benoît,67
p2=Micheline,Benoît,67
p3=Jean,Dupont,30

وقتی متغیر p1 با استفاده از

dim p1 as personne=new personne("Jean","Dupont",30)

p1 به شیء personne("Jean","Dupont",30) اشاره می‌کند اما خود شیء نیست. در C می‌گوییم که این یک اشارهگر (pointer) به نشانی شیء ایجادشده است. اگر سپس بنویسیم:

    p1=nothing

این شیء personne("Jean","Dupont",30) نیست که تغییر می‌کند؛ بلکه مرجع p1 است که مقدار آن تغییر می‌کند. شیء personne("Jean","Dupont",30) «گم» خواهد شد اگر توسط هیچ متغیر دیگری ارجاع داده نشود.

وقتی می‌نویسید:

dim p2 as personne=p1

اشاره‌گر p2 مقداردهی اولیه می‌شود: این اشاره‌گر به همان شیئی اشاره می‌کند (یا به همان شیئی ارجاع می‌دهد) که اشاره‌گر p1 به آن اشاره می‌کند. بنابراین، اگر ما شیء «اشاره‌شده» (یا مرجع) توسط p1 را تغییر دهیم، در واقع همان شیء مرجع توسط p2 را تغییر می‌دهیم.

وقتی می‌نویسیم:

dim p3 as personne =new personne(p1);

یک شیء جدید ایجاد می‌شود که کپی‌ای از شیء مرجع p1 است. این شیء جدید توسط p3 مرجع‌دهی خواهد شد. اگر شیء «اشاره‌شده» (یا مرجع) توسط p1 تغییر یابد، شیء مرجع p3 کاملاً بدون تغییر باقی می‌ماند. این موضوع با نتایج به‌دست‌آمده نشان داده شده است.

3.1.11. ابجکت‌های موقت

در یک عبارت، می‌توان به‌طور صریح سازنده (constructor) یک شیء را فراخوانی کرد: شیء ایجاد می‌شود، اما ما به آن دسترسی نداریم (برای مثال برای تغییر آن). این شیء موقت برای هدف ارزیابی عبارت ایجاد می‌شود و سپس دور ریخته می‌شود. فضای حافظه‌ای که اشغال کرده بود، بعداً به‌طور خودکار توسط برنامه‌ای به نام «جمع‌کننده زباله» (garbage collector) بازیابی می‌شود که وظیفه‌اش بازیابی فضای حافظه‌ای است که توسط اشیایی که دیگر توسط داده‌های برنامه ارجاع داده نمی‌شوند، اشغال شده است. بیایید برنامه آزمایشی جدید زیر را در نظر بگیریم:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

' صفحهٔ آزمایشی
Module test
    Sub Main()
        Dim p As New personne(New personne("Jean", "Dupont", 30))
        p.identifie()
    End Sub
End Module

و بیایید سازنده‌های کلاس personne را طوری تغییر دهیم که پیامی را نمایش دهند:


    ' سازنده‌ها
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        Console.Out.WriteLine("Constructeur personne(String, String, integer)")
        initialise(P, N, age)
    End Sub

    Public Sub New(ByVal P As personne)
        Console.Out.WriteLine("Constructeur personne(personne)")
        initialise(P)
    End Sub

نتایج زیر را به دست می‌آوریم:

dos>test
Constructeur personne(String, String, integer)
Constructeur personne(personne)
Jean,Dupont,30

نشان‌دهنده ساخت متوالی دو شیء موقت.

3.1.12. متدهای خواندن و نوشتن ویژگی‌های خصوصی

ما متدهای لازم را به کلاس personne برای خواندن یا تغییر وضعیت ویژگی‌های اشیاء اضافه می‌کنیم:


Imports System

Public Class personne

    ' ویژگی‌ها
    Private prenom As [String]
    Private nom As [String]
    Private age As Integer

    ' سازنده‌ها
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub

    Public Sub New(ByVal P As personne)
        Me.prenom = P.prenom
        Me.nom = P.nom
        Me.age = P.age
    End Sub

    ' شناسه‌ها
    Public Sub identifie()
        Console.Out.WriteLine((prenom + "," + nom + "," + age))
    End Sub

    ' دسترس‌کننده‌ها
    Public Function getPrenom() As [String]
        Return prenom
    End Function

    Public Function getNom() As [String]
        Return nom
    End Function

    Public Function getAge() As Integer
        Return age
    End Function

    'تعدیل‌کننده‌ها
    Public Sub setPrenom(ByVal P As [String])
        Me.prenom = P
    End Sub

    Public Sub setNom(ByVal N As [String])
        Me.nom = N
    End Sub

    Public Sub setAge(ByVal age As Integer)
        Me.age = age
    End Sub
End Class

ما کلاس جدید را با برنامه زیر آزمایش می‌کنیم:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

' صفحه آزمایشی
Public Module test
    Sub Main()
        Dim P As New personne("Jean", "Michelin", 34)
        Console.Out.WriteLine(("P=(" & P.getPrenom() & "," & P.getNom() & "," & P.getAge() & ")"))
        P.setAge(56)
        Console.Out.WriteLine(("P=(" & P.getPrenom() & "," & P.getNom() & "," & P.getAge() & ")"))
    End Sub
End Module

و نتایج زیر را به دست می‌آوریم:

P=(Jean,Michelin,34)
P=(Jean,Michelin,56)

3.1.13. ویژگی‌ها

راه دیگری برای دسترسی به ویژگی‌های یک کلاس وجود دارد: با ایجاد پراپرتی‌ها. این پراپرتی‌ها به ما امکان می‌دهند ویژگی‌های خصوصی را طوری دستکاری کنیم که گویی عمومی هستند. کلاس زیر personne را در نظر بگیرید که در آن گترها و سترهای قبلی با پراپرتی‌های قابل‌خواندن و نوشتن جایگزین شده‌اند:


' گزینه‌ها
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

' کلاس شخص
Public Class personne

    ' ویژگی‌ها
    Private _prenom As [String]
    Private _nom As [String]
    Private _age As Integer


    '
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        Me._prenom = P
        Me._nom = N
        Me._age = age
    End Sub


    Public Sub New(ByVal P As personne)
        Me._prenom = P._prenom
        Me._nom = P._nom
        Me._age = P._age
    End Sub

    ' شناسایی می‌کند
    Public Sub identifie()
        Console.Out.WriteLine((_prenom & "," & _nom & "," & _age))
    End Sub

    ' ویژگی‌ها
    Public Property prenom() As String
        Get
            Return _prenom
        End Get
        Set(ByVal Value As String)
            _prenom = Value
        End Set
    End Property

    Public Property nom() As String
        Get
            Return _nom
        End Get
        Set(ByVal Value As String)
            _nom = Value
        End Set
    End Property

    Public Property age() As Integer
        Get
            Return _age
        End Get
        Set(ByVal Value As Integer)
            ' آیا سن معتبر است؟
            If Value >= 0 Then
                _age = Value
            Else
                Throw New Exception("âge (" & Value & ") invalide")
            End If
        End Set
    End Property
End Class

یک ویژگی به شما امکان می‌دهد مقدار یک صفت را بخوانید (دریافت کنید) یا آن را تنظیم کنید. در مثال ما، نام‌های صفات را با یک زیرخط (_) شروع کرده‌ایم تا ویژگی‌ها نام‌های یکسانی با صفات اولیه داشته باشند. این به این دلیل است که یک ویژگی نمی‌تواند نام یکسانی با صفتی که مدیریت می‌کند داشته باشد؛ در غیر این صورت، در داخل کلاس یک تداخل نامی رخ خواهد داد. بنابراین، ما ویژگی‌های خود را _prenom، _nom و _age نام‌گذاری کرده و سازنده‌ها و متدها را متناسب با آن تغییر دادیم. سپس سه ویژگی ایجاد کردیم: nom، prenom و age. یک ویژگی به صورت زیر تعریف می‌شود:


    Public Property nom() As Type
        Get
...
        End Get
        Set(ByVal Value As Type)
...
        End Set    
    End Property

که در آن Type باید نوع صفتی باشد که توسط خاصیت مدیریت می‌شود. این ممکن است دو متد به نام‌های get و set داشته باشد. متد get معمولاً مسئول بازگرداندن مقدار صفتی است که مدیریت می‌کند (می‌تواند چیز دیگری را بازگرداند؛ چیزی مانع این کار نیست). روش set پارامتری به نام value می‌گیرد که معمولاً آن را به ویژگی‌ای که مدیریت می‌کند اختصاص می‌دهد. این روش می‌تواند از این فرصت استفاده کند تا اعتبار مقدار دریافتی را بررسی کند و در صورت لزوم، اگر مقدار نامعتبر باشد، یک استثنا (exception) پرتاب (throw) کند. این کاری است که در اینجا برای سن انجام شده است.

این متدهای get و set چگونه فراخوانی می‌شوند؟ بیایید برنامه آزمایشی زیر را در نظر بگیریم:


' گزینه‌ها
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

' صفحهٔ آزمایشی
Module test
    Sub Main()
        Dim P As New personne("Jean", "Michelin", 34)
        Console.Out.WriteLine(("P=(" & P.prenom & "," & P.nom & "," & P.age & ")"))
        P.age = 56
        Console.Out.WriteLine(("P=(" & P.prenom & "," & P.nom & "," & P.age & ")"))
        Try
            P.age = -4
        Catch ex As Exception
            Console.Error.WriteLine(ex.Message)
        End Try
    End Sub
End Module

در عبارت

        Console.Out.WriteLine(("P=(" & P.prenom & "," & P.nom & "," & P.age & ")"))

ما در پی به دست آوردن مقادیر ویژگی‌های prenom، nom و age برای شخص P هستیم. این متد get این ویژگی‌ها است که فراخوانی می‌شود و مقدار ویژگی‌ای را که مدیریت می‌کند، برمی‌گرداند.

در عبارت

        P.age = 56

ما می‌خواهیم مقدار ویژگی age را تنظیم کنیم. سپس متد set این ویژگی فراخوانی می‌شود. این متد مقدار 56 را به عنوان پارامتر value خود دریافت خواهد کرد.

یک ویژگی P از کلاس C که فقط متد get را تعریف می‌کند، ویژگی فقط-خواندنی (read-only) نامیده می‌شود. اگر c یک شیء از کلاس C باشد، عملیات c.P=value توسط کامپایلر رد خواهد شد.

اجرای برنامه آزمایشی قبلی نتایج زیر را تولید می‌کند:

P=(Jean,Michelin,34)
P=(Jean,Michelin,56)
âge (-4) invalide

بنابراین، ویژگی‌ها به ما امکان می‌دهند تا ویژگی‌های خصوصی را طوری دستکاری کنیم که گویی عمومی هستند.

3.1.14. متدها و ویژگی‌های کلاس

فرض کنید بخواهیم تعداد اشیاء [personne] ایجادشده در یک برنامه را بشماریم. می‌توانیم خودمان یک شمارنده را مدیریت کنیم، اما در معرض این خطر هستیم که اشیاء موقتی را که اینجا و آنجا ایجاد می‌شوند نادیده بگیریم. به نظر می‌رسد امن‌تر باشد که در سازنده‌های کلاس [personne] یک عبارت را قرار دهیم که شمارنده را افزایش می‌دهد. مشکل، ارسال یک مرجع به این شمارنده است تا سازنده بتواند آن را افزایش دهد: باید یک پارامتر جدید به آن ارسال شود. همچنین می‌توان شمارنده را در تعریف کلاس گنجاند. از آنجا که این یک ویژگی (attribute) خود کلاس است و نه ویژگی یک شیء خاص از آن کلاس، به شیوه‌ای متفاوت با استفاده از کلمه کلیدی Shared اعلام می‌شود:

    Private Shared _nbPersonnes As Long = 0

برای ارجاع به آن، personne._nbPersonnes را می‌نویسیم تا نشان دهیم که این یک ویژگی از خود کلاس personne است. در اینجا، ما یک ویژگی خصوصی ایجاد کرده‌ایم که از خارج کلاس به آن دسترسی مستقیم وجود ندارد. بنابراین یک ویژگی عمومی ایجاد می‌کنیم تا دسترسی به ویژگی کلاس nbPersonnes را فراهم کنیم. برای تعیین مقدار nbPersonnes، متد get این خاصیت به یک شیء personne خاص نیاز ندارد: در واقع، _nbPersonnes ویژگی یک شیء خاص نیست؛ بلکه ویژگی یک کلاس کامل است. بنابراین، ما همچنین به یک ویژگی با اعلامیه‌ای مشابه Shared نیاز داریم:

    Public Shared ReadOnly Property nbPersonnes() As Long
        Get
            Return _nbPersonnes
        End Get
    End Property

که از خارج با استفاده از نحو personne.nbPersonnes فراخوانی خواهد شد. این ویژگی به‌صورت فقط‌خواندنی (ReadOnly) اعلام شده است زیرا متد تعیین مقدار ندارد. در اینجا یک مثال آمده است. کلاس personne به شکل زیر درمی‌آید:


Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

'کلاس
Public Class personne
    ' ویژگی‌های کلاس
    Private Shared _nbPersonnes As Long = 0

    ' ویژگی‌های نمونه
    Private _prenom As [String]
    Private _nom As [String]
    Private _age As Integer

    ' سازنده‌ها
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        ' یک شخص دیگر
        _nbPersonnes += 1
        Me._prenom = P
        Me._nom = N
        Me._age = age
    End Sub

    Public Sub New(ByVal P As personne)
        ' یک شخص دیگر
        _nbPersonnes += 1
        Me._prenom = P._prenom
        Me._nom = P._nom
        Me._age = P._age
    End Sub

    ' شناسایی می‌کند
    Public Sub identifie()
        Console.Out.WriteLine((_prenom & "," & _nom & "," & _age))
    End Sub

    ' ویژگی کلاس
    Public Shared ReadOnly Property nbPersonnes() As Long
        Get
            Return _nbPersonnes
        End Get
    End Property

    ' ویژگی‌های نمونه
    Public Property prenom() As String
        Get
            Return _prenom
        End Get
        Set(ByVal Value As String)
            _prenom = Value
        End Set
    End Property

    Public Property nom() As String
        Get
            Return _nom
        End Get
        Set(ByVal Value As String)
            _nom = Value
        End Set
    End Property

    Public Property age() As Integer
        Get
            Return _age
        End Get
        Set(ByVal Value As Integer)
            ' سن معتبر؟
            If Value >= 0 Then
                _age = Value
            Else
                Throw New Exception("âge (" & Value & ") invalide")
            End If
        End Set
    End Property
End Class

با برنامه زیر:


' گزینه‌ها
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

' پایگاه داده آزمایشی
Module test
    Sub Main()
        Dim p1 As New personne("Jean", "Dupont", 30)
        Dim p2 As New personne(p1)
        Console.Out.WriteLine(("Nombre de personnes créées : " & personne.nbPersonnes))
    End Sub
End Module

نتایج زیر به دست می‌آیند:

    Nombre de personnes créées : 2

3.1.15. ارسال یک شیء به یک تابع

ما قبلاً اشاره کرده‌ایم که، به طور پیش‌فرض، VB.NET پارامترهای واقعی یک تابع را به صورت ارزشی منتقل می‌کند: مقادیر پارامترهای واقعی در پارامترهای اسمی کپی می‌شوند. در مورد یک شیء، نباید از سوءاستفاده سیستماتیک از اصطلاحات گمراه شد، جایی که از اصطلاح «شیء» به جای «اشاره‌گر شیء» استفاده می‌شود. یک شیء تنها از طریق یک مرجع (اشاره‌گر) به آن قابل دستکاری است. بنابراین آنچه به یک تابع ارسال می‌شود خودِ شیء نیست، بلکه مرجعی به آن شیء است. در نتیجه، این مقدار مرجع است که به پارامتر شکلی کپی می‌شود، نه مقدار خودِ شیء: هیچ شیء جدیدی ایجاد نمی‌شود. اگر یک مرجع شیء R1 به یک تابع ارسال شود، آن در پارامتر شکلی متناظر R2 کپی خواهد شد. در نتیجه، ارجاعات R2 و R1 به یک شیء واحد اشاره می‌کنند. اگر تابع شیء اشاره شده توسط R2 را تغییر دهد، واضح است که همان شیء اشاره شده توسط R1 را نیز تغییر می‌دهد، زیرا این دو یکی هستند.

این موضوع با مثال زیر نشان داده می‌شود:


' گزینه‌ها
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

' صفحه آزمایشی
Module test
    Sub Main()
        ' یک شخص p1
        Dim p1 As New personne("Jean", "Dupont", 30)

        ' نمایش p1
        Console.Out.Write("Paramètre effectif avant modification : ")
        p1.identifie()

        ' ویرایش p1
        modifie(p1)

        ' نمایش p1
        Console.Out.Write("Paramètre effectif après modification : ")
        p1.identifie()
    End Sub

    Sub modifie(ByVal P As personne)
        ' نمایش شخص P
        Console.Out.Write("Paramètre formel avant modification : ")
        P.identifie()
        'ویرایش P
        P.prenom = "Sylvie"
        P.nom = "Vartan"
        P.age = 52
        'نمایش P
        Console.Out.Write("Paramètre formel après modification : ")
        P.identifie()
    End Sub
End Module

نتایج به‌دست‌آمده به شرح زیر است:

Paramètre effectif avant modification : Jean,Dupont,30
Paramètre formel avant modification : Jean,Dupont,30
Paramètre formel après modification : Sylvie,Vartan,52
Paramètre effectif après modification : Sylvie,Vartan,52

می‌توان دید که تنها یک شیء ایجاد شده است: شیء مربوط به شخص p1 از رویه Main، و این که شیء واقعاً توسط تابع modifie تغییر داده شده است.

3.1.16. یک آرایه از افراد

یک شیء مانند هر دادهٔ دیگری است و به همین دلیل، می‌توان چندین شیء را در یک آرایه گروه‌بندی کرد:


' گزینه‌ها
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System

' صفحهٔ آزمایشی
Module test
    Sub Main()
        ' جدولی از افراد
        Dim amis(2) As personne
        amis(0) = New personne("Jean", "Dupont", 30)
        amis(1) = New personne("Sylvie", "Vartan", 52)
        amis(2) = New personne("Neil", "Armstrong", 66)
        ' نمایش
        Console.Out.WriteLine("----------------")
        Dim i As Integer
        For i = 0 To amis.Length - 1
            amis(i).identifie()
        Next i
    End Sub
End Module

دستور Dim friends(2) As person یک آرایه با ۳ عنصر از نوع personne ایجاد می‌کند. این ۳ عنصر در اینجا با مقادیر nothing و c.a.d مقداردهی اولیه شده‌اند، به این معنی که به هیچ شیئی ارجاع نمی‌دهند. بار دیگر، در معنایی کلی، وقتی به یک «آرایه از اشیاء» اشاره می‌کنیم، در واقع صرفاً با یک آرایه از ارجاعات به اشیاء سروکار داریم. ایجاد آرایهٔ اشیاء — که خود یک شیء است — هیچ شیء جدیدی از نوع عناصر خود ایجاد نمی‌کند: این کار باید به صورت جداگانه انجام شود. نتایج زیر به دست می‌آیند:

Jean,Dupont,30
Sylvie,Vartan,52
Neil,Armstrong,66

3.2. ارث‌بری به مثاله

3.2.1. عمومی

در اینجا مفهوم ارث‌بری را بررسی می‌کنیم. هدف از ارث‌بری، «سفارشی‌سازی» یک کلاس موجود است تا نیازهای ما را برآورده کند. فرض کنید می‌خواهیم کلاسی به نام enseignant ایجاد کنیم: معلم یک نوع خاص از شخص است. آن‌ها ویژگی‌هایی دارند که دیگران ندارند: برای مثال، موضوعی که تدریس می‌کنند. اما آن‌ها همچنین ویژگی‌های مشترک همه افراد را دارند: نام، نام خانوادگی و سن. بنابراین، یک معلم عضو کامل کلاس personne است اما ویژگی‌های اضافی نیز دارد. به جای ایجاد یک کلاس enseignant از صفر، ترجیح می‌دهیم بر روی ساختار موجود کلاس personne بنا کنیم و آن را با ویژگی‌های خاص معلمان تطبیق دهیم. این مفهوم ارث‌بری است که به ما این امکان را می‌دهد. برای بیان اینکه کلاس enseignant از ویژگی‌های کلاس personne ارث می‌برد، می‌نویسیم:


Public Class enseignant
    Inherits personne

به سینتکس خاص دو خطی توجه کنید. کلاس personne کلاس والد (یا مادر) و کلاس enseignant کلاس مشتق (یا دختر) نامیده می‌شود. یک شیء از نوع enseignant تمام ویژگی‌های یک شیء از نوع personne را داراست: این شیء دارای همان ویژگی‌ها و متدها است. این ویژگی‌ها و متدهای کلاس والد در تعریف کلاس فرزند تکرار نمی‌شوند؛ در عوض، تنها ویژگی‌ها و متدهایی که توسط کلاس فرزند اضافه شده‌اند، مشخص می‌شوند. ما فرض می‌کنیم که کلاس personne به صورت زیر تعریف شده است:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

' کلاس شخص
Public Class personne

    ' ویژگی‌های کلاس
    Private Shared _nbPersonnes As Long = 0

    ' ویژگی‌های نمونه
    Private _prenom As [String]
    Private _nom As [String]
    Private _age As Integer


    ' سازنده‌ها
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        ' یک شخص دیگر
        _nbPersonnes += 1
        ' ساختمان
        Me._prenom = P
        Me._nom = N
        Me._age = age
        ' ردیابی
        Console.Out.WriteLine("Construction personne(string, string, int)")
    End Sub


    Public Sub New(ByVal P As personne)
        ' یک شخص دیگر
        _nbPersonnes += 1
        'ساخت
        Me._prenom = P._prenom
        Me._nom = P._nom
        Me._age = P._age
        ' نظارت
        Console.Out.WriteLine("Construction personne(string, string, int)")
    End Sub

    ' ویژگی کلاس
    Public Shared ReadOnly Property nbPersonnes() As Long
        Get
            Return _nbPersonnes
        End Get
    End Property

    ' ویژگی‌های نمونه
    Public Property prenom() As String
        Get
            Return _prenom
        End Get
        Set(ByVal Value As String)
            _prenom = Value
        End Set
    End Property

    Public Property nom() As String
        Get
            Return _nom
        End Get
        Set(ByVal Value As String)
            _nom = Value
        End Set
    End Property

    Public Property age() As Integer
        Get
            Return _age
        End Get
        Set(ByVal Value As Integer)
            ' سن معتبر؟
            If Value >= 0 Then
                _age = Value
            Else
                Throw New Exception("âge (" & Value & ") invalide")
            End If
        End Set
    End Property

    Public ReadOnly Property identite() As String
        Get
            Return "personne(" & _prenom & "," & _nom & "," & age & ")"
        End Get
    End Property
End Class

متد identifie با خاصیت فقط-خواندنی identité که شخص را شناسایی می‌کند، جایگزین شده است. ما یک کلاس enseignant را از کلاس personne ارث می‌بریم:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Public Class enseignant
    Inherits personne
    ' ویژگی‌ها
    Private _section As Integer

    ' سازنده
    Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
        MyBase.New(P, N, age)
        Me._section = section
        ' ردیابی
        Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
    End Sub

    ' ویژگی بخش
    Public Property section() As Integer
        Get
            Return _section
        End Get
        Set(ByVal Value As Integer)
            _section = Value
        End Set
    End Property
End Class 

کلاس enseignant به متدها و ویژگی‌های کلاس personne اضافه می‌کند:

  • یک ویژگی section، که شماره بخشی است که معلم در میان کادر تدریس به آن تعلق دارد (تقریباً هر درس یک بخش)
  • یک سازندهٔ جدید برای مقداردهی اولیهٔ تمام ویژگی‌های یک معلم

اعلام


Public Class enseignant
    Inherits personne

نشان می‌دهد که کلاس enseignant از کلاس personne مشتق شده است.

3.2.2. ایجاد یک شیء معلم

سازنده کلاس enseignant به شرح زیر است:


    ' سازنده
    Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
        MyBase.New(P, N, age)
        Me._section = section
         ' ردیابی
        Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
    End Sub    

اعلامیه


    Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)

مشخص می‌کند که سازنده چهار پارامتر می‌گیرد: P، N، age و section. باید سه مورد از آن‌ها، (P,N,age)، را به کلاس پایه خود، در این مورد کلاس personne، ارسال کند. می‌دانیم که این کلاس دارای یک سازنده person(string, string, int) است که امکان ایجاد یک شخص را با استفاده از پارامترهای ارسال‌شده، (P,N,age)، فراهم می‌کند. کلاس [enseignant] پارامترهای (P, N, age) را به کلاس پایه خود به صورت زیر ارسال می‌کند:


        MyBase.New(P, N, age)

پس از نمونه‌سازی کلاس پایه، نمونه‌سازی شی enseignant با اجرای بدنهٔ سازنده ادامه می‌یابد:


        Me._section = section

خلاصه اینکه، سازنده یک کلاس مشتق:

  • پارامترهایی را که برای ساخت خود نیاز دارد به کلاس پایه خود ارسال می‌کند
  • از سایر پارامترها برای مقداردهی اولیه ویژگی‌های خود استفاده می‌کند

ممکن بود ترجیح داده شود که به این صورت نوشته شود:


    Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
        Me._prenom = P
        Me._nom = N
        Me._age = age
        Me._section = section
        ' ردیابی
        Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
    End Sub    

این امکان‌پذیر نیست. کلاس personne سه فیلد خود _prenom، _nom و _age را به صورت خصوصی (private) اعلام کرده است. فقط اشیاء همان کلاس به این فیلدها دسترسی مستقیم دارند. تمام اشیاء دیگر، از جمله اشیاء فرزند در این مورد، باید برای دسترسی به آن‌ها از متدهای عمومی استفاده کنند. این وضعیت متفاوت می‌شد اگر کلاس personne سه فیلد را به صورت محافظت‌شده (protected) اعلام می‌کرد (protected): در این صورت به کلاس‌های مشتق اجازه دسترسی مستقیم به این سه فیلد داده می‌شد. بنابراین در مثال ما، استفاده از سازنده کلاس والد، راه‌حل صحیح و رویکرد استاندارد بود: هنگام ساخت یک شیء فرزند، ابتدا سازنده شیء والد فراخوانی می‌شود و سپس مراحل اولیه خاص شیء فرزند (section در مثال ما) تکمیل می‌گردد.

بیایید کلاس‌های personne و enseignant را به اسمبلی‌ها کامپایل کنیم:

dos>vbc /t:library personne.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573

dos>vbc /r:personne.dll /t:library enseignant.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573

dos>dir
25/02/2004  10:08                1 828 personne.vb
25/02/2004  10:11                  675 enseignant.vb
25/02/2004  10:12                  223 test.vb
25/02/2004  10:16                4 096 personne.dll
25/02/2004  10:16                3 584 enseignant.dll

توجه داشته باشید که برای کامپایل کلاس فرزند enseignant، لازم بود به فایل personne.dll که حاوی کلاس personne است، ارجاع داده شود. بیایید یک برنامه آزمایشی اول را امتحان کنیم:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Module test
    Sub Main()
        Console.Out.WriteLine(New enseignant("Jean", "Dupont", 30, 27).identite)
    End Sub
End Module

این برنامه به سادگی یک شیء enseignant جدید ایجاد می‌کند و آن را شناسایی می‌کند. کلاس enseignant متدی به نام identité ندارد، اما کلاس والد آن چنین متدی دارد که آن نیز عمومی است: از طریق ارث‌بری، این متد به یک متد عمومی در کلاس enseignant تبدیل می‌شود. نتایج به‌دست‌آمده به شرح زیر است:

dos>vbc /r:personne.dll /r:enseignant.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573

dos>test
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
personne(Jean,Dupont,30)

می‌توانیم ببینیم که:

  • یک شیء personne قبل از شیء enseignant ایجاد شده است
  • هویت به‌دست‌آمده، هویت شیء personne است

3.2.3. سرشارژ کردن یک متد یا خاصیت

در مثال قبلی، ما شناسه بخش personne از معلم را داشتیم، اما جزئیات خاصی که مختص کلاس enseignant (بخش) بود، وجود نداشت. بنابراین ما باید یک ویژگی برای شناسایی معلم تعریف کنیم:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Public Class enseignant
    Inherits personne
    ' ویژگی‌ها
    Private _section As Integer

    ' سازنده
    Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
        MyBase.New(P, N, age)
        Me._section = section
        ' ردیابی
        Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
    End Sub

    ' ویژگی بخش
    Public Property section() As Integer
        Get
            Return _section
        End Get
        Set(ByVal Value As Integer)
            _section = Value
        End Set
    End Property

    ' خاصیت بازنویسی هویت
    Public Shadows ReadOnly Property identite() As String
        Get
            Return "enseignant(" & MyBase.identite & "," & _section & ")"
        End Get
    End Property
End Class

متد identite از کلاس enseignant به متد identite از کلاس والد خود (MyBase.identite) متکی است. برای نمایش بخش «personne» خود، سپس این را با فیلد _section که مختص کلاس enseignant است، تکمیل می‌کند. توجه داشته باشید که اعلان خاصیت identite:


    Public Shadows ReadOnly Property identite() As String

که نشان می‌دهد ویژگی «identite» متد هم‌نام را که ممکن است در کلاس والد وجود داشته باشد، «پنهان» می‌کند. یک شیء E از نوع enseignant را در نظر بگیرید. این شیء درون خود یک شیء personne را در بر دارد:

ویژگی identity هم در کلاس enseignant و هم در کلاس والد آن، personne، تعریف شده است. در کلاس فرزند enseignant، ویژگی identite باید با کلمه کلیدی «shadows» پیش‌فرض قرار گیرد تا نشان دهد که یک ویژگی جدید، identite، برای کلاس enseignant. مجدداً تعریف می‌شود.


    Public Shadows ReadOnly Property identite() As String

کلاس enseignant اکنون دارای دو ویژگی identite است:

  • یکی ارث‌برده از کلاس والد personne
  • خودش

اگر E یک شیء از نوع enseignant باشد، E.identite به متد identite از کلاس enseignant اشاره می‌کند. گفته می‌شود که ویژگی identite از کلاس والد توسط ویژگی identite از کلاس فرزند «پوشانده شده» است. به‌طور کلی، اگر O یک شیء و M یک متد باشد، برای اجرای متد O.M، سیستم به دنبال متدی با نام M به ترتیب زیر می‌گردد:

  • در کلاس شیء O
  • در کلاس والد آن، اگر وجود داشته باشد
  • در کلاس والد کلاس والد آن، اگر وجود داشته باشد
  • و غیره…

وراثت بنابراین اجازه می‌دهد که متدها و ویژگی‌هایی با همان نامِ متدها و ویژگی‌های کلاس والد، در کلاس فرزند دوباره تعریف شوند. این همان چیزی است که کلاس فرزند را قادر می‌سازد تا با نیازهای خود تطبیق یابد. ترکیبِ بارگذاری مجددِ متدها و ویژگی‌ها با چندریختی (polymorphism) که کمی بعد به آن خواهیم پرداخت، اصلی‌ترین مزیتِ وراثت است. بیایید همان مثالِ قبل را در نظر بگیریم:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Module test
    Sub Main()
        Console.Out.WriteLine(New enseignant("Jean", "Dupont", 30, 27).identite)
    End Sub
End Module

این بار، نتایج به شرح زیر است:

Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant(personne(Jean,Dupont,30),27)

3.2.4. پلیمورفیسم

یک تبار از کلاس‌ها را در نظر بگیرید: C0C1C2 … جایی که CiCj نشان می‌دهد کلاس Cj از کلاس Ci مشتق شده است. این بدان معناست که کلاس Cj دارای تمام ویژگی‌های کلاس Ci به‌علاوه ویژگی‌های دیگر است. فرض کنید Oi اشیایی از نوع Ci باشند. نوشتن زیر مجاز است:

    Oi=Oj avec j>i

در واقع، با ارث‌بری، کلاس Cj تمام ویژگی‌های کلاس Ci به علاوه ویژگی‌های دیگر را دارد. بنابراین، یک شیء Oj از نوع Cj در درون خود یک شیء از نوع Ci را در بر دارد. عمل

Oi=Oj

به این معنی است که Oi یک مرجع به شی از نوع Ci است که در شی Oj جای گرفته است.

این واقعیت که یک متغیر Oi از کلاس Ci در واقع می‌تواند نه تنها به یک شیء از کلاس Ci بلکه به هر شیئی که از کلاس Ci مشتق شده است، اشاره کند، به عنوان چندریختی (polymorphism) شناخته می‌شود: توانایی یک متغیر برای اشاره به انواع مختلف اشیاء. بیایید یک مثال بزنیم و تابع زیر را در نظر بگیریم که به هیچ کلاسی وابسته نیست:


    Sub affiche(ByVal p As personne)

ما می‌توانیم به همان آسانی بنویسیم

    dim p as personne
    ...
    affiche(p);

به عنوان

    dim e as enseignant
    ...
    affiche(e);

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

3.2.5. تعریف مجدد و چندریختگی

بیایید رویهٔ affiche خود را تکمیل کنیم:


    Sub affiche(ByVal p As personne)
         ' نمایش p هویت
        Console.Out.WriteLine(p.identite)
    End Sub    

متد p.identite رشته‌ای را بازمی‌گرداند که شیء personne. را شناسایی می‌کند. در مثال قبلی ما، که شیء آن enseignant بود، چه اتفاقی می‌افتد:


        Dim e As New enseignant("Lucile", "Dumas", 56, 61)
        affiche(e)

بیایید به مثال زیر نگاه کنیم:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Module test
    Sub Main()
        ' یک معلم
        Dim e As New enseignant("Lucile", "Dumas", 56, 61)
        affiche(e)
        ' یک شخص
        Dim p As New personne("Jean", "Dupont", 30)
        affiche(p)
    End Sub

    ' نمایش‌ها
    Sub affiche(ByVal p As personne)
        ' هویت p را نمایش می‌دهد
        Console.Out.WriteLine(p.identite)
    End Sub
End Module

نتایج به‌دست‌آمده به شرح زیر است:

Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
personne(Lucile,Dumas,56)
Construction personne(string, string, int)
personne(Jean,Dupont,30)

اجرا نشان می‌دهد که دستور p.identite هر بار خاصیت identite از یک personne را اجرا کرد، شخصی که در e از enseignant قرار داشت، و سپس خود p از personne. این به شیء واقعی که به‌عنوان پارامتر به affiche ارسال شده بود، تطبیق نیافت. ما ترجیح می‌دادیم که هویت کامل enseignant e را داشته باشیم. برای اینکه این اتفاق می‌افتاد، باید نشانه p.identite به ویژگی identite شیئی که در واقع توسط p به آن اشاره می‌شود، ارجاع می‌داد، نه به ویژگی identite بخشی "personne" از شیء‌ای که در واقع توسط p به آن اشاره می‌شود. این نتیجه را می‌توان با اعلام identite به‌عنوان یک ویژگی قابل بازنویسی در کلاس پایه personne به‌دست آورد:


    Public Overridable ReadOnly Property identite() As String
        Get
            Return "personne(" & _prenom & "," & _nom & "," & age & ")"
        End Get
    End Property

کلمه کلیدی overridable، **identite را به یک ویژگی قابل جایگزینی (overridable) یا مجازی تبدیل می‌کند. این کلمه کلیدی را می‌توان به متدها نیز اعمال کرد. کلاس‌های فرزند که یک ویژگی یا متد مجازی را بازنویسی می‌کنند، بنابراین باید به جای **shadows از کلمه کلیدی overrides برای مشخص کردن ویژگی یا متد بازنویسی‌شده خود استفاده کنند. بنابراین، در کلاس enseignant، ویژگی identite به شرح زیر تعریف شده است:


    ' تعریف مجدد ویژگی هویت
    Public Overrides ReadOnly Property identite() As String
        Get
            Return "enseignant(" & MyBase.identite & "," & _section & ")"
        End Get
    End Property

برنامهٔ آزمون:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Module test
    Sub Main()
        ' یک معلم
        Dim e As New enseignant("Lucile", "Dumas", 56, 61)
        affiche(e)
        ' یک شخص
        Dim p As New personne("Jean", "Dupont", 30)
        affiche(p)
    End Sub

    'نمایش‌ها
    Sub affiche(ByVal p As personne)
        ' پوستر هویت
        Console.Out.WriteLine(p.identite)
    End Sub
End Module

سپس نتایج زیر را تولید می‌کند:

Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant(personne(Lucile,Dumas,56),61)
Construction personne(string, string, int)
personne(Jean,Dupont,30)

این بار، ما با موفقیت هویت کامل معلم را به‌دست آوردیم. اکنون بیایید به‌جای یک ویژگی، یک متد را مجدداً تعریف کنیم. کلاس object کلاس «پدر» تمام کلاس‌های VB.NET است. بنابراین وقتی می‌نویسیم:

    public class personne

در واقع به‌طور ضمنی داریم می‌نویسیم:

    public class personne 
         inherits object

کلاس object یک متد مجازی ToString را تعریف می‌کند:

Image

متد ToString نام کلاسی را که شیء به آن تعلق دارد بازمی‌گرداند، همان‌طور که در مثال زیر نشان داده شده است:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Module test2
    Sub Main()
        ' یک معلم
        Console.Out.WriteLine(New enseignant("Lucile", "Dumas", 56, 61).ToString())
        ' یک شخص
        Console.Out.WriteLine(New personne("Jean", "Dupont", 30).ToString())
    End Sub
End Module

نتایج تولید شده به شرح زیر است:

Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant
Construction personne(string, string, int)
personne

شایان ذکر است که اگرچه ما متد ToString را در کلاس‌های personne و enseignant مجدداً تعریف نکرده‌ایم، با این حال می‌توانیم ببینیم که متد ToString در کلاس object همچنان قادر به نمایش نام واقعی کلاس شیء است. بیایید متد ToString را در کلاس‌های personne و enseignant مجدداً تعریف کنیم:


    ' ToString
    Public Overrides Function ToString() As String
        ' ما ویژگی «هویت» را ایجاد می‌کنیم
        Return identite
    End Function

تعریف در هر دو کلاس یکسان است. برنامهٔ آزمون زیر را در نظر بگیرید:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Module test
    Sub Main()
        ' یک معلم
        Dim e As New enseignant("Lucile", "Dumas", 56, 61)
        affiche(e)
        ' یک شخص
        Dim p As New personne("Jean", "Dupont", 30)
        affiche(p)
    End Sub

    ' نمایش‌ها
    Sub affiche(ByVal p As personne)
        ' پوستر هویت
        Console.Out.WriteLine(p.identite)
    End Sub
End Module

نتایج اجرا به شرح زیر است:

Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant(personne(Lucile,Dumas,56),61)
Construction personne(string, string, int)
personne(Jean,Dupont,30)

3.3. تعریف یک ایندکس‌کننده برای یک کلاس

بیایید کلاس از پیش تعریف‌شده [ArrayList] را در پلتفرم NET در نظر بگیریم. این کلاس برای ذخیره اشیاء در یک لیست استفاده می‌شود. این کلاس به فضای نام [System.Collections] تعلق دارد. در مثال زیر، از این کلاس برای ذخیره فهرستی از افراد (در وسیع‌ترین معنا) استفاده می‌کنیم:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System
Imports System.Collections

' کلاس listeDePersonnes
Public Class listeDePersonnes
    Inherits ArrayList

    'برای افزودن یک شخص به فهرست
    Public Overloads Sub Add(ByVal p As personne)
        MyBase.Add(p)
    End Sub

    ' یک ایندکس‌کننده
    Default Public Shadows Property Item(ByVal i As Integer) As personne
        Get
            Return CType(MyBase.Item(i), personne)
        End Get
        Set(ByVal Value As personne)
            MyBase.Item(i) = Value
        End Set
    End Property

    ' شاخص‌گذار دیگر
    Default Public Shadows ReadOnly Property Item(ByVal N As String) As Integer
        Get
            ' جستجو برای فردی به نام N
            Dim i As Integer
            For i = 0 To Count - 1
                If CType(Me(i), personne).nom = N Then
                    Return i
                End If
            Next i
            Return -1
        End Get
    End Property

    'toString
    Public Overrides Function ToString() As String
        ' مقادیر (element1, element2, ..., elementn) را بازمی‌گرداند
        Dim liste As String = "("
        Dim i As Integer
        ' تکرار در آرایهٔ پویا
        For i = 0 To (Count - 2)
            liste += "[" + Me(i).ToString + "]" + ","
        Next i        'for
        ' آخرین عنصر
        If Count <> 0 Then
            liste += "[" + Me(i).ToString + "]"
        End If
        liste += ")"
        Return liste
    End Function
End Class

بیایید به برخی از ویژگی‌ها و متدهای کلاس [ArrayList] نگاهی بیندازیم:

Count
ویژگی که تعداد عناصر موجود در لیست را نشان می‌دهد
Add(Object)
متد برای افزودن یک شیء به لیست
Item(Integer i)
متدی که عنصر i-ام لیست را برمی‌گرداند

توجه داشته باشید که برای بازیابی عنصر i-ام لیست، ما به جای [liste.Item(i)]، مستقیماً [liste(i)] را نوشته‌ایم که در نگاه اول نادرست به نظر می‌رسد. با این حال این کار ممکن است زیرا کلاس [ArrayList] یک ویژگی پیش‌فرض [Item] را با نحوه‌ای مشابه زیر تعریف می‌کند:


    Default Public Property Item(ByVal i As Integer) As Object
        Get
...
        End Get
        Set(ByVal Value As personne)
...
        End Set    
    End Property

وقتی کامپایلر با نشانه [liste(i)] مواجه می‌شود، بررسی می‌کند که آیا کلاس [ArrayList] خاصیتی با امضای زیر تعریف کرده است:


    Default Public Property Proc(ByVal var As Integer) As Type

در اینجا، رویه [Item] را پیدا خواهد کرد. سپس نشانه [liste(i)] را به [liste.Item(i)] تبدیل خواهد کرد. ویژگی [Item] به‌عنوان ویژگی شاخص‌گذاری‌شدهٔ پیش‌فرض کلاس [ArrayList] شناخته می‌شود. اجرای برنامهٔ فوق نتایج زیر را تولید می‌کند:

dos>vbc /r:personne.dll /r:enseignant.dll lstpersonnes1.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
11/03/2004  18:26             3 584 enseignant.dll
12/03/2004  15:34             3 584 lstpersonnes1.exe
12/03/2004  13:39               661 lstpersonnes1.vb
11/03/2004  18:26             4 096 personne.dll
dos>lstpersonnes1
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
personne(paul,chenou,31)
personne(nicole,chenou,11)
enseignant(personne(jacques,sileau,33),61)

بیایید یک کلاس به نام [listeDePersonnes] ایجاد کنیم که شامل فهرستی از افراد باشد – نوع خاصی از فهرست که به نظر می‌رسد به‌طور طبیعی از کلاس [ArrayList] مشتق شود:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System
Imports System.Collections

' کلاس listeDePersonnes
Public Class listeDePersonnes
    Inherits ArrayList

    ' برای افزودن یک شخص به فهرست
    Public Shadows Sub Add(ByVal p As Object)
        ' p باید یک شخص باشد
        If Not TypeOf (p) Is personne Then
            Throw New Exception("L'objet ajouté (" + p.ToString + ") n'est pas une personne")
        Else
            MyBase.Add(p)
        End If
    End Sub

    ' یک نمایه ساز
    Default Public Shadows Property Index(ByVal i As Integer) As personne
        Get
            Return CType(MyBase.Item(i), personne)
        End Get
        Set(ByVal Value As personne)
            MyBase.Item(i) = Value
        End Set
    End Property

    ' toString
    Public Overrides Function ToString() As String
        ' مقادیر (element1, element2, ..., elementn) را بازمی‌گرداند
        Dim liste As String = "("
        Dim i As Integer
        ' ما روی آرایهٔ پویا حلقه‌زنی می‌کنیم
        For i = 0 To (Count - 2)
            liste += "[" + Me(i).ToString + "]" + ","
        Next i        'for
        ' آخرین عنصر
        If Count <> 0 Then
            liste += "[" + Me(i).ToString + "]"
        End If
        liste += ")"
        Return liste
    End Function
End Class

این کلاس دارای متدها و ویژگی‌های زیر است:

ToString
یک رشته را بازمی‌گرداند که «نماینده» محتوای لیست است
Add(personne)
یک متد برای افزودن یک شخص به لیست
Item(Integer i)
ویژگی پیش‌فرض فهرست‌شده که فرد i-ام را در لیست بازمی‌گرداند

بیایید ویژگی‌های جدید را بررسی کنیم:

یک متد جدید به نام [Add] ایجاد شده است.


     ' برای افزودن یک شخص به لیست
    Public Shadows Sub Add(ByVal p As Object)
        'p باید یک شخص باشد
        If Not TypeOf (p) Is personne Then
            Throw New Exception("L'objet ajouté (" + p.ToString + ") n'est pas une personne")
        Else
            MyBase.Add(p)
        End If
    End Sub

متدی با همین امضا در کلاس والد [ArrayList] وجود دارد؛ بنابراین از کلمه کلیدی [Shadows] استفاده می‌شود تا نشان دهد که رویه جدید جایگزین رویه موجود در کلاس والد است. متد [Add] در کلاس فرزند بررسی می‌کند که شیء افزوده‌شده واقعاً از نوع [personne] است یا از آن ارث‌بری شده است، با استفاده از تابع [TypeOf]. اگر اینطور نباشد، با استفاده از دستور [Throw] یک استثنا پرتاب می‌شود. اگر شیء در حال اضافه شدن واقعاً از نوع [personne] باشد، از متد [Add] کلاس پایه برای انجام افزودن استفاده می‌شود.

یک ویژگی شاخص‌گذاری‌شده ایجاد می‌شود:


    ' شاخص‌ساز
    Default Public Shadows Property Index(ByVal i As Integer) As personne
        Get
            Return CType(MyBase.Item(i), personne)
        End Get
        Set(ByVal Value As personne)
            MyBase.Item(i) = Value
        End Set
    End Property

در کلاس پایه، از قبل یک ویژگی پیش‌فرض به نام [Item] با همان امضا وجود دارد. بنابراین ما مجبوریم از کلمه کلیدی [Shadows] استفاده کنیم تا نشان دهیم که ویژگی جدید فهرست‌شده [Index]، ویژگی موجود در کلاس پایه، [Item] را «پنهان» خواهد کرد. توجه داشته باشید که این موضوع حتی زمانی که دو خاصیت نام یکسانی ندارند نیز صادق است. ویژگی [Index] برای ارجاع به شماره فرد i در لیست استفاده می‌شود. این ویژگی برای دسترسی به عنصر شماره i از شی زیرین [ArrayList]، به ویژگی [Item] از کلاس پایه متکی است. تغییرات نوع برای در نظر گرفتن این واقعیت اعمال می‌شود که ویژگی [Item] با عناصر از نوع [Object] کار می‌کند، در حالی که ویژگی [Index] با عناصر از نوع [personne] کار می‌کند.

در نهایت، ما متد [ToString] از کلاس [ArrayList] را بازنویسی می‌کنیم:


    ' toString
    Public Overrides Function ToString() As String
        ' مقادیر (element1, element2, ..., elementn) را بازمی‌گرداند
        Dim liste As String = "("
        Dim i As Integer
        ' ما روی آرایهٔ پویا حلقه می‌زنیم
        For i = 0 To (Count - 2)
            liste += "[" + Me(i).ToString + "]" + ","
        Next i        'for
         ' آخرین عنصر
        If Count <> 0 Then
            liste += "[" + Me(i).ToString + "]"
        End If
        liste += ")"
        Return liste
    End Function

این متد یک رشته از نوع «(e1, e2, …, en)» را بازمی‌گرداند، که در آن e1، e2، …، en عناصر لیست هستند. توجه داشته باشید به قرارداد [Me(i)] که به عنصر شماره i از شیء فعلی [Me] اشاره می‌کند. از خاصیت فهرست‌شده پیش‌فرض استفاده می‌شود. بنابراین، [Me(i)] معادل [Me.Index(i)] است.

کد کلاس در فایل [lstpersonnes2.vb] قرار داده شده و کامپایل می‌شود:

dos>dir
11/03/2004  18:26             3 584 enseignant.dll
12/03/2004  15:45               970 lstpersonnes2.vb
11/03/2004  18:26             4 096 personne.dll
dos>vbc /r:personne.dll /r:enseignant.dll /t:library lstpersonnes2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
11/03/2004  18:26             3 584 enseignant.dll
12/03/2004  15:50             3 584 lstpersonnes2.dll
12/03/2004  15:45               970 lstpersonnes2.vb
11/03/2004  18:26             4 096 personne.dll

یک برنامه آزمون ایجاد می‌شود:


' گزینه‌ها
Option Explicit On 
Option Strict On

' فضاهای نام
Imports System
Imports System.Collections

Module test
    Sub Main()
        ' ایجاد یک لیست خالی از افراد
        Dim liste As listeDePersonnes = New listeDePersonnes
        ' ایجاد افراد
        Dim p1 As personne = New personne("paul", "chenou", 31)
        Dim p2 As personne = New personne("nicole", "chenou", 11)
        Dim e1 As enseignant = New enseignant("jacques", "sileau", 33, 61)
        ' پر کردن فهرست
        liste.Add(p1)
        liste.Add(p2)
        liste.Add(e1)
        ' نمایش لیست
        Console.Out.WriteLine(liste.ToString)
        ' افزودن یک شی غیرانسانی
        Try
            liste.Add(4)
        Catch e As Exception
            Console.Error.WriteLine(e.Message)
        End Try
    End Sub
End Module

کامپایل می‌شود:

dos>vbc /r:personne.dll /r:enseignant.dll /r:lstpersonnes2.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
11/03/2004  18:26             3 584 enseignant.dll
12/03/2004  15:50             3 584 lstpersonnes2.dll
12/03/2004  15:45               970 lstpersonnes2.vb
11/03/2004  18:26             4 096 personne.dll
12/03/2004  15:50             3 584 test.exe
12/03/2004  15:49               623 test.vb

سپس اجرا شد:

dos>test
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
([personne(paul,chenou,31)],[personne(nicole,chenou,11)],[enseignant(personne(jacques,sileau,33),61)])
L'objet ajouté (4) n'est pas une personne

ممکن است بخواهید بنویسید

dim p as personne=l("nom")

که در آن l از نوع [listeDePersonnes] خواهد بود. در اینجا می‌خواهیم فهرست l را نه با شماره عنصر بلکه با نام شخص ایندکس کنیم. برای این کار، یک ویژگی جدید را به‌صورت پیش‌فرض ایندکس‌شده تعریف می‌کنیم:


    ' شاخص‌گذار دیگر
    Default Public Shadows ReadOnly Property Item(ByVal N As String) As Integer
        Get
            'جستجو برای شخص با نام N
            Dim i As Integer
            For i = 0 To Count - 1
                If CType(Me(i), personne).nom = N Then
                    Return i
                End If
            Next i
            Return -1
        End Get
    End Property

خط اول


    Default Public Shadows ReadOnly Property Index(ByVal N As String) As Integer

نشان می‌دهد که ما در حال ایجاد یک ویژگی پیش‌فرض نمایه‌شده جدید هستیم. تمام ویژگی‌های پیش‌فرض باید نام یکسانی داشته باشند، در این مورد [Index]. ویژگی جدید [Index] کلاس listeDePersonnes را با رشته کاراکتری N نمایه می‌کند. نتیجه listeDePersonnes(N) یک عدد صحیح است. این عدد صحیح، موقعیت شخص با نام N در لیست خواهد بود، یا در صورتی که آن شخص در لیست نباشد، مقدار -1 را برمی‌گرداند. فقط خاصیت get تعریف شده است، و بدین ترتیب از استفاده از listeDePersonnes جلوگیری می‌کند (استفاده از "nom")=valeur مستلزم تعریف خاصیت set بود). از این رو کلمه کلیدی [ReadOnly]. کلمه کلیدی [Shadows] برای پنهان کردن ویژگی پیش‌فرض کلاس پایه لازم است (هرچند که امضای یکسانی ندارد).

در بدنه get، لیست افراد برای یافتن نام N که به‌عنوان پارامتر ارسال شده است، جستجو می‌شود. اگر در موقعیت i یافت شود، i بازگردانده می‌شود؛ در غیر این صورت، -1 بازگردانده می‌شود.

یک برنامه آزمایشی جدید می‌تواند به شکل زیر باشد:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

' صفحه آزمایشی
Module test
    Sub Main()
        ' فهرستی از افراد
        Dim l As New listeDePersonnes
        ' افزودن افراد
        l.Add(New personne("jean", "dumornet", 10))
        l.Add(New personne("pauline", "duchemin", 12))
        ' نمایش
        Console.Out.WriteLine(("l=" + l.ToString))
        l.Add(New personne("jacques", "tartifume", 27))
        Console.Out.WriteLine(("l=" + l.ToString))
        ' تغییر آیتم ۱
        l(1) = New personne("sylvie", "cachan", 5)
        'نمایش آیتم 1
        Console.Out.WriteLine(("l[1]=" + l(1).ToString))
        ' نمایش لیست l
        Console.Out.WriteLine(("l=" + l.ToString))
        ' جستجوی افراد
        Dim noms() As String = New [String]() {"cachan", "inconnu"}
        Dim i As Integer
        For i = 0 To noms.Length - 1
            Dim inom As Integer = l(noms(i))
            If inom <> -1 Then
                Console.Out.WriteLine(("personne(" & noms(i) & ")=" & l(inom).ToString))
            Else
                Console.Out.WriteLine(("personne(" + noms(i) + ") n'existe pas"))
            End If
        Next i
    End Sub    
End Module

اجرا نتایج زیر را تولید می‌کند:

personne(string, string, int)
Construction personne(string, string, int)
l=([personne(jean,dumornet,10)],[personne(pauline,duchemin,12)])
Construction personne(string, string, int)
l=([personne(jean,dumornet,10)],[personne(pauline,duchemin,12)],[personne(jacques,tartifume,27)])
Construction personne(string, string, int)
l[1]=personne(sylvie,cachan,5)
l=([personne(jean,dumornet,10)],[personne(sylvie,cachan,5)],[personne(jacques,tartifume,27)])
personne(cachan)=personne(sylvie,cachan,5)
personne(inconnu) n'existe pas

3.4. ساختارها

ساختار VB.NET مستقیماً از ساختار زبان C مشتق شده و شباهت زیادی به یک کلاس دارد. یک ساختار به صورت زیر تعریف می‌شود:


Structure spersonne
' ویژگی‌ها
    ...
' ویژگی‌ها
...
' سازنده‌ها
...
' روش‌ها
End Structure

با وجود شباهت‌ها در نحو، تفاوت‌های قابل توجهی بین کلاس‌ها و ساختارها وجود دارد. برای مثال، مفهوم ارث‌بری در ساختارها وجود ندارد. اگر در حال نوشتن کلاسی هستیم که قرار نیست از کلاسی دیگر مشتق شود، چه تفاوت‌هایی بین ساختارها و کلاس‌ها وجود دارد که به ما در انتخاب بین این دو کمک کند؟ بیایید برای پی بردن به این موضوع، از مثال زیر استفاده کنیم:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

'ساختار spersonne
Structure spersonne
    Public nom As String
    Public age As Integer
End Structure

' کلاس `cpersonne`
Class cpersonne
    Public nom As String
    Public age As Integer
End Class

' یک ماژول آزمایشی
Public Module test
    Sub Main()
        ' یک شخص p1
        Dim sp1 As spersonne
        sp1.nom = "paul"
        sp1.age = 10
        Console.Out.WriteLine(("sp1=spersonne(" & sp1.nom & "," & sp1.age & ")"))
        ' یک شخص p2
        Dim sp2 As spersonne = sp1
        Console.Out.WriteLine(("sp2=spersonne(" & sp2.nom & "," & sp2.age & ")"))
        'sp2 اصلاح شده است
        sp2.nom = "nicole"
        sp2.age = 30
        'تأیید sp1 و sp2
        Console.Out.WriteLine(("sp1=cpersonne(" & sp1.nom & "," & sp1.age & ")"))
        Console.Out.WriteLine(("sp2=cpersonne(" & sp2.nom & "," & sp2.age & ")"))

        ' یک شخص cp1
        Dim cp1 As New cpersonne
        cp1.nom = "paul"
        cp1.age = 10
        Console.Out.WriteLine(("cP1=cpersonne(" & cp1.nom & "," & cp1.age & ")"))
        ' یک cperson P2
        Dim cp2 As cpersonne = cp1
        Console.Out.WriteLine(("cP2=cpersonne(" & cp2.nom & "," & cp2.age & ")"))
        ' P2 اصلاح شده است
        cp2.nom = "nicole"
        cp2.age = 30
        'تأیید P1 و P2
        Console.Out.WriteLine(("cP1=cpersonne(" & cp1.nom & "," & cp1.age & ")"))
        Console.Out.WriteLine(("cP2=cpersonne(" & cp2.nom & "," & cp2.age & ")"))
    End Sub
End Module

اگر این برنامه را اجرا کنید، نتایج زیر را دریافت می‌کنید:

sp1=spersonne(paul,10)
sp2=spersonne(paul,10)
sp1=cpersonne(paul,10)
sp2=cpersonne(nicole,30)
cP1=cpersonne(paul,10)
cP2=cpersonne(paul,10)
cP1=cpersonne(nicole,30)
cP2=cpersonne(nicole,30)

در حالی که در صفحات قبلی این فصل از کلاس personne استفاده کردیم، اکنون از ساختار spersonne استفاده می‌کنیم:


'ساختار شخص
Structure spersonne
    Public nom As String
    Public age As Integer
End Structure

اعلامیه


        Dim sp1 As spersonne

یک ساختار (نام، سن) ایجاد می‌کند و مقدار sp1 خود این ساختار است.

اعلام


        Dim cp1 As New cpersonne

یک شیء [cpersonne] (تقریباً معادل ساختار ما) ایجاد می‌کند و cp1 سپس آدرس (اشاره) این شیء است.

خلاصه

  • در مورد ساختار، مقدار sp1 خودِ ساختار است
  • در مورد کلاس، مقدار p1 نشانی شیء ایجادشده است

وقتی موارد زیر در برنامه نوشته می‌شود


        Dim sp2 As spersonne = sp1

یک ساختار جدید (name, age) ایجاد شده و با مقدار p1، یعنی خود ساختار، مقداردهی اولیه می‌شود.

بنابراین ساختار در sp1 در sp2 کپی می‌شود. این یک کپی به صورت مقدار است.

دستور


        Dim cp2 As cpersonne = cp1

به شکل متفاوتی عمل می‌کند. مقدار cp1 به cp2 کپی می‌شود، اما از آنجا که این مقدار در واقع نشانی شیء است، خود شیء کپی نمی‌شود. این شیء صرفاً دو مرجع دارد که به آن اشاره می‌کنند:

در مورد ساختار، اگر مقدار sp2 تغییر کند، مقدار sp1 تغییر نمی‌کند، همانطور که در برنامه نشان داده شده است. در مورد شیء، اگر شیئی که توسط cp2 به آن اشاره می‌شود تغییر یابد، آنی که توسط QZXX2HTMLP000650ZQX به آن اشاره می‌شود نیز تغییر می‌یابد، زیرا آن‌ها یکی هستند. این موضوع نیز توسط نتایج برنامه اثبات می‌شود.

بنابراین می‌توانیم از این توضیحات نتیجه بگیریم که:

  • مقدار یک متغیر از نوع «ساختار» خودِ ساختار است
  • مقدار یک متغیر از نوع 'object' نشانی شیئی است که به آن اشاره می‌کند

هنگامی که این تفاوت اساسی درک شود، ساختار بسیار شبیه به یک کلاس از آب می‌آید، همانطور که مثال جدید زیر نشان می‌دهد:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

'ساختار شخص
Structure personne
    ' ویژگی‌ها
    Private _nom As String
    Private _age As Integer

    ' ویژگی‌ها
    Public Property nom() As String
        Get
            Return _nom
        End Get
        Set(ByVal Value As String)
            _nom = value
        End Set
    End Property

    Public Property age() As Integer
        Get
            Return _age
        End Get
        Set(ByVal Value As Integer)
            _age = value
        End Set
    End Property

    ' سازنده
    Public Sub New(ByVal NOM As String, ByVal AGE As Integer)
        _nom = NOM
        _age = AGE
    End Sub    'New

    ' TOSTRING
    Public Overrides Function ToString() As String
        Return "personne(" & nom & "," & age & ")"
    End Function
End Structure

' یک ماژول آزمایشی
Module test
    Sub Main()
        ' یک شخص p1
        Dim p1 As New personne("paul", 10)
        Console.Out.WriteLine(("p1=" & p1.ToString))
        ' یک شخص p2
        Dim p2 As personne = p1
        Console.Out.WriteLine(("p2=" & p2.ToString))
        ' p2 اصلاح شده است
        p2.nom = "nicole"
        p2.age = 30
        'بررسی p1 و p2
        Console.Out.WriteLine(("p1=" & p1.ToString))
        Console.Out.WriteLine(("p2=" & p2.ToString))
    End Sub
End Module

نتایج اجرای زیر به دست می‌آیند:

p1=personne(paul,10)
p2=personne(paul,10)
p1=personne(paul,10)
p2=personne(nicole,30)

تنها تفاوت قابل توجه در اینجا بین یک ساختار و یک کلاس این است که در مورد کلاس، اشیاء p1 و p2 در پایان برنامه دارای یک مقدار یکسان، یعنی مقدار p2، خواهند بود.

3.5. رابط‌ها

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

Public Interface Istats
    Function moyenne() As Double
    Function écartType() As Double
End Interface

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

public class C 
 Implements Istats
...
function moyenne() as Double Implements Istats.moyenne
...
end function
function écartType () as Double Implements Istats. écartType
...
end function
end class

متدهای [moyenne] و [écartType] باید در کلاس C تعریف شوند. کد زیر را در نظر بگیرید:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

'ساختار
Public Structure élève
    Public _nom As String
    Public _note As Double

    'سازنده
    Public Sub New(ByVal NOM As String, ByVal NOTE As Double)
        Me._nom = NOM
        Me._note = NOTE
    End Sub
End Structure

' کلاس یادداشت‌ها
Public Class notes
    ' ویژگی
    Protected _matière As String
    Protected _élèves() As élève

    ' تولیدکننده
    Public Sub New(ByVal MATIERE As String, ByVal ELEVES() As élève)
        ' سوابق دانش‌آموز و درس
        Me._matière = MATIERE
        Me._élèves = ELEVES
    End Sub

    ' ToString
    Public Overrides Function ToString() As String
        Dim valeur As String = "matière=" + _matière + ", notes=("
        Dim i As Integer
        ' ادغام تمام نمرات
        For i = 0 To (_élèves.Length - 1) - 1
            valeur &= "[" & _élèves(i)._nom & "," & _élèves(i)._note & "],"
        Next i
        'نمره نهایی
        If _élèves.Length <> 0 Then
            valeur &= "[" & _élèves(i)._nom & "," & _élèves(i)._note & "]"
        End If
        valeur += ")"
        ' پایان
        Return valeur
    End Function
End Class

کلاس notes نشان‌ها را برای یک کلاس در یک موضوع گرد هم می‌آورد:


Public Class notes
     ' ویژگی
    Protected _matière As String
    Protected _élèves() As élève

ویژگی‌ها به صورت protected اعلام شده‌اند تا از یک کلاس مشتق‌شده قابل دسترسی باشند. نوع élève یک ساختار است که نام دانش‌آموز و نمره او را در آن درس ذخیره می‌کند:


Public Structure élève
    Public _nom As String
    Public _note As Double

     ' سازنده
    Public Sub New(ByVal NOM As String, ByVal NOTE As Double)
        Me._nom = NOM
        Me._note = NOTE
    End Sub    
End Structure

ما تصمیم گرفته‌ایم این کلاس، notes، را به کلاسی، notesStats، مشتق کنیم که دو ویژگی اضافی خواهد داشت: میانگین و انحراف معیار نمرات:


Public Class notesStats
    Inherits notes
    Implements Istats

     'ویژگی‌ها
    Private _moyenne As Double
    Private _écartType As Double

کلاس notesStats رابط زیر Istats را پیاده‌سازی می‌کند:

Public Interface Istats
    Function moyenne() As Double
    Function écartType() As Double
End Interface

این بدان معناست که کلاس notesStats باید دارای دو متد به نام‌های moyenne و écartType با امضای مشخص‌شده در رابط Istats باشد. کلاس notesStats به شرح زیر است:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Public Class notesStats
    Inherits notes
    Implements Istats

    ' ویژگی‌ها
    Private _moyenne As Double
    Private _écartType As Double

    ' سازنده
    Public Sub New(ByVal MATIERE As String, ByVal ELEVES() As élève)
        MyBase.New(MATIERE, ELEVES)
        'محاسبه نمره میانگین
        Dim somme As Double = 0
        Dim i As Integer
        For i = 0 To ELEVES.Length - 1
            somme += ELEVES(i)._note
        Next i
        If ELEVES.Length <> 0 Then
            _moyenne = somme / ELEVES.Length
        Else
            _moyenne = -1
        End If
        ' انحراف معیار
        Dim carrés As Double = 0
        For i = 0 To ELEVES.Length - 1
            carrés += Math.Pow(ELEVES(i)._note - _moyenne, 2)
        Next i
        If ELEVES.Length <> 0 Then
            _écartType = Math.Sqrt((carrés / ELEVES.Length))
        Else
            _écartType = -1
        End If
    End Sub

    ' ToString
    Public Overrides Function ToString() As String
        Return MyBase.ToString() & ",moyenne=" & _moyenne & ",écart-type=" & _écartType
    End Function    'ToString

    'روش‌های رابط Istats
    Public Function moyenne() As Double Implements Istats.moyenne
        'میانگین نمرات را محاسبه می‌کند
        Return _moyenne
    End Function

    Public Function écartType() As Double Implements Istats.écartType
        'انحراف معیار را بازمی‌گرداند
        Return _écartType
    End Function
End Class

میانگین _moyenne و انحراف معیار _ecartType به محض ایجاد شیء محاسبه می‌شوند. بنابراین، متدهای moyenne و écartType به سادگی مقادیر ویژگی‌های _moyenne و _ecartType را بازمی‌گردانند. هر دو متد در صورتی که آرایهٔ دانش‌آموزان خالی باشد، مقدار -1 را بازمی‌گردانند.

کلاس تست زیر:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Module test
    Sub Main()
        ' برخی دانش‌آموزان و نمرات
        Dim ELEVES() As élève = {New élève("paul", 14), New élève("nicole", 16), New élève("jacques", 18)}
        'که در یک شیء «marks» ذخیره شده‌اند
        Dim anglais As New notes("anglais", ELEVES)
        ' و که نمایش داده می‌شوند
        Console.Out.WriteLine((anglais.ToString))
        ' همان، با میانگین و انحراف معیار
        anglais = New notesStats("anglais", ELEVES)
        Console.Out.WriteLine((anglais.ToString))
    End Sub
End Module

نتایج زیر را تولید می‌کند:

matière=anglais, notes=([paul,14],[nicole,16],[jacques,18])
matière=anglais, notes=([paul,14],[nicole,16],[jacques,18]),moyenne=16,écart-type=1,63299316185545

کلاس notesStats می‌توانست به راحتی متدهای moyenne و écartType را برای خود پیاده‌سازی کند، بدون اینکه مشخص کند که رابط Istats را پیاده‌سازی می‌کند. هدف از رابط‌ها چیست؟ هدف به این صورت است: یک تابع می‌تواند به‌عنوان پارامتر صوری، مقداری از نوع یک رابط I را بپذیرد. هر شیء از کلاس C که رابط I را پیاده‌سازی کند، می‌تواند سپس به‌عنوان پارامتر حقیقی به آن تابع ارسال شود. مثال زیر را در نظر بگیرید:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

'یک رابط Iexample
Public Interface Iexemple
    Function ajouter(ByVal i As Integer, ByVal j As Integer) As Integer
    Function soustraire(ByVal i As Integer, ByVal j As Integer) As Integer
End Interface

' یک کلاس اول
Public Class classe1
    Implements Iexemple

    Public Function ajouter(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.ajouter
        Return a + b + 10
    End Function

    Public Function soustraire(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.soustraire
        Return a - b + 20
    End Function
End Class

'یک کلاس دوم
Public Class classe2
    Implements Iexemple

    Public Function ajouter(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.ajouter
        Return a + b + 100
    End Function

    Public Function soustraire(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.soustraire
        Return a - b + 200
    End Function
End Class

رابط Iexemple دو متد را تعریف می‌کند: ajouter و soustraire. کلاس‌های classe1 و classe2 این رابط را پیاده‌سازی می‌کنند. توجه داشته باشید که این کلاس‌ها کار دیگری انجام نمی‌دهند؛ این کار برای ساده‌ نگه‌داشتن مثال است. اکنون مثال زیر را در نظر بگیرید:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

'کلاس آزمایشی
Module test

    'محاسبه
    Sub calculer(ByVal i As Integer, ByVal j As Integer, ByVal inter As Iexemple)
        Console.Out.WriteLine(inter.ajouter(i, j))
        Console.Out.WriteLine(inter.soustraire(i, j))
    End Sub

    ' تابع Main
    Sub Main()
        ' ایجاد دو شیء، class1 و class2
        Dim c1 As New classe1
        Dim c2 As New classe2
        ' فراخوانی تابع استاتیک 'calculate'
        calculer(4, 3, c1)
        calculer(14, 13, c2)
    End Sub
End Module

تابع calculer یک عنصر از نوع Iexemple را به عنوان پارامتر می‌پذیرد. بنابراین این تابع می‌تواند برای این پارامتر یک شیء از نوع classe1 و همچنین یک شیء از نوع classe2 را بپذیرد. این کاری است که در رویه Main انجام می‌شود، با نتایج زیر:

17
21
127
201

بنابراین می‌توانیم ببینیم که این یک ویژگی مشابه چندریختی (polymorphism) مشاهده‌شده در کلاس‌ها است. اگر مجموعه‌ای از کلاس‌های Ci که از طریق ارث‌بری به یکدیگر مرتبط نیستند (و بنابراین نمی‌توانیم از چندریختی مبتنی بر ارث‌بری استفاده کنیم) مجموعه‌ای از متدها با امضای یکسان را به اشتراک بگذارند، ممکن است مفید باشد که این متدها را در یک رابط (interface) به نام I گروه‌بندی کنیم که همه کلاس‌های مرتبط از آن ارث‌بری کنند. اشیاء این کلاس‌های Ci سپس می‌توانند به‌عنوان پارامتر برای توابعی که پارامتری از نوع I می‌پذیرند، استفاده شوند، c.a.d. این توابع تنها از متدهای اشیاء Ci که در رابط I تعریف شده‌اند استفاده می‌کنند و نه از ویژگی‌ها و متدهای خاص کلاس‌های مختلف Ci. در نهایت، توجه داشته باشید که ارث‌بری چندگانه از اینترفیس امکان‌پذیر است، c.a.d. ما می‌توانیم بنویسیم


Public Class classe
    Implements I1,I2,...

که در آن Ij اینترفیس‌ها هستند.

3.6. فضاهای نام

برای نوشتن یک خط روی صفحه، از دستور

Console.Out.WriteLine(...)

اگر به تعریف کلاس Console نگاه کنیم


Namespace: System
Assembly: Mscorlib (in Mscorlib.dll)

متوجه می‌شویم که به فضای نام System تعلق دارد. این بدان معناست که کلاس Console باید به عنوان System.Console ارجاع داده شود، و در واقع باید بنویسیم:

System.Console.Out.WriteLine(...)

می‌توان با استفاده از یک بند imports از این مشکل جلوگیری کرد:

imports System
...
Console.Out.WriteLine(...)

ما می‌گوییم که با استفاده از بند imports، فضای نام System را وارد می‌کنیم. وقتی کامپایلر با نام یک کلاس (در این مورد، Console) مواجه می‌شود، آن را در فضاهای نام مختلف واردشده توسط بندهای imports جستجو می‌کند. در اینجا، کلاس Console را در فضای نام System پیدا خواهد کرد. اکنون بیایید به دومین اطلاعات مرتبط با کلاس Console توجه کنیم:


Assembly: Mscorlib (in Mscorlib.dll)

این خط نشان می‌دهد که تعریف کلاس Console در کدام «اسمبلی» قرار دارد. هنگام کامپایل خارجی Studio.NET و زمانی که نیاز دارید ارجاعات به فایل‌های مختلف dll را که حاوی کلاس‌های مورد نیاز شما هستند، مشخص کنید، این اطلاعات می‌تواند مفید باشد. به خاطر داشته باشید که برای ارجاع به فایل‌های dll مورد نیاز برای کامپایل یک کلاس، می‌نویسید:

vbc /r:fic1.dll /r:fic2.dll ... prog.vb

هنگام ایجاد یک کلاس، می‌توان آن را در داخل یک فضای نام ایجاد کرد. هدف از این نام‌فضاها، جلوگیری از تداخل نام بین کلاس‌ها هنگام فروش آن‌ها است. برای مثال، دو شرکت E1 و E2 را در نظر بگیرید که به ترتیب کلاس‌هایی را در dll بسته‌بندی و توزیع می‌کنند، E1.dll و E2.dll به‌ترتیب. فرض کنیم مشتری C این دو مجموعه کلاس را خریداری می‌کند که در هر دو شرکت کلاسی به نام personne تعریف شده است. مشتری C برنامه‌ای را به شرح زیر کامپایل می‌کند:

vbc /r:E1.dll /r:E2.dll prog.vb

اگر سورس prog.vb از کلاس personne استفاده کند، کامپایلر نخواهد دانست که آیا باید از کلاس personne در E1.dll استفاده کند یا از کلاس موجود در E2.dll. این امر منجر به خطا خواهد شد. اگر شرکت E1 دقت کند که کلاس‌های خود را در یک فضای نام به نام E1 ایجاد کند و شرکت E2 در یک فضای نام به نام E2، دو کلاس personne سپس با نام‌های E1.personne و E2.personne نام‌گذاری خواهند شد. کلاینت باید در کلاس‌های خود از E1.personne یا E2.personne استفاده کند، اما نه از personne. فضای نام این ابهام را برطرف می‌کند. برای ایجاد یک کلاس در یک فضای نام، به این صورت می‌نویسند:


Namespace istia.st
    Public Class personne
        'تعریف کلاس
...
  end Class
end Namespace

برای مثال، بیایید کلاس personne را که قبلاً درباره‌اش بحث کردیم، در یک فضای نام ایجاد کنیم. ما istia.st را به‌عنوان فضای نام انتخاب می‌کنیم. کلاس personne به این صورت درمی‌آید:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

' ایجاد فضای نام istia.st
Namespace istia.st
    Public Class personne
        ' ویژگی‌ها
        Private prenom As String
        Private nom As String
        Private age As Integer

        ' روش
        Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
            Me.prenom = P
            Me.nom = N
            Me.age = age
        End Sub

        ' روش
        Public Sub identifie()
            Console.Out.WriteLine((prenom & "," & nom & "," & age))
        End Sub
    End Class
End Namespace

این کلاس به personne.dll کامپایل می‌شود:

dos>dir
11/03/2004  18:27               610 personne.vb
dos>vbc /t:library personne.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
12/03/2004  18:06             3 584 personne.dll
11/03/2004  18:27               610 personne.vb

اکنون بیایید کلاس personne را در یک کلاس تست استفاده کنیم:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System
Imports istia.st

' صفحه آزمایشی
Public Module test
    Sub Main()
        Dim p1 As New personne
        p1.initialise("Jean", "Dupont", 30)
        p1.identifie()
    End Sub
End Module

برای جلوگیری از نوشتن


        Dim p1 As New istia.st.personne

ما با استفاده از یک بند imports، فضای نام istia.st را وارد کرده‌ایم:


Imports istia.st

اکنون برنامهٔ آزمایشی را کامپایل کنیم:

dos>dir
12/03/2004  18:06             3 584 personne.dll
11/03/2004  18:27               610 personne.vb
12/03/2004  18:05               254 test.vb
dos>vbc /r:personne.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
12/03/2004  18:06             3 584 personne.dll
11/03/2004  18:27               610 personne.vb
12/03/2004  18:08             3 072 test.exe
12/03/2004  18:05               254 test.vb

این یک فایل به نام test.exe تولید می‌کند که با اجرای آن، نتایج زیر به دست می‌آید:

Jean,Dupont,30

3.7. مثال IMPOTS

ما به محاسبه مالیات که پیش‌تر در فصل قبل پوشش داده شده بود بازمی‌گردیم و آن را با استفاده از یک کلاس پردازش می‌کنیم. بیایید مسئله را مرور کنیم:

ما مورد ساده‌شده‌ی یک مؤدی را در نظر می‌گیریم که تنها حقوق خود را برای اظهارنامه دارد:

  • تعداد سهام کارمند را به صورت nbParts = nbEnfants/2 + 1 در صورتی که متأهل نباشد، محاسبه می‌کنیم، nbEnfants/2 + 2 اگر متأهل باشند، که در آن nbEnfants تعداد فرزندان اوست.
  • اگر او حداقل سه فرزند داشته باشد، مستحق دریافت نیم سهم اضافی است.
  • درآمد مشمول مالیات او به صورت R = 0.72 × S محاسبه می‌شود، که در آن S حقوق سالانه او است
  • ضریب خانوادگی او به صورت QF = R / nbParts محاسبه می‌شود
  • مالیات I او محاسبه می‌شود. جدول زیر را در نظر بگیرید:
۱۲٬۶۲۰٫۰
0
0
۱۳٬۱۹۰
0.05
۶۳۱
15,640
0.1
۱٬۲۹۰٫۵
۲۴٬۷۴۰
0.15
۲٬۰۷۲٫۵
۳۱٬۸۱۰
0.2
۳٬۳۰۹٫۵
۳۹٬۹۷۰
0.25
۴٬۹۰۰
۴۸٬۳۶۰
0.3
۶۸۹۸.۵
۵۵٬۷۹۰
0.35
۹٬۳۱۶٫۵
۹۲٬۹۷۰
0.4
۱۲٬۱۰۶
۱۲۷٬۸۶۰
0.45
۱۶٬۷۵۴٫۵
۱۵۱٬۲۵۰
0.50
۲۳٬۱۴۷٫۵
۱۷۲٬۰۴۰
0.55
۳۰٬۷۱۰
۱۹۵٬۰۰۰
0.60
۳۹٬۳۱۲
0
0.65
۴۹٬۰۶۲

هر سطر دارای ۳ فیلد است. برای محاسبه مالیات I، به دنبال اولین سطری می‌گردیم که QFfield1 باشد. برای مثال، اگر QF = 23000 باشد، سطر را پیدا خواهیم کرد

    24740        0.15        2072.5

مالیه I در این صورت برابر است با 0.15*R - 2072.5*nbParts. اگر QF به گونه‌ای باشد که شرط QF <= field1 هرگز برقرار نشود، ضرایب مربوط به آخرین خط استفاده می‌شوند. در این صورت:

    0                0.65        49062

که مالیات I را به صورت 0.65*R - 49062*nbParts تعیین می‌کند.

کلاس «impot» به صورت زیر تعریف خواهد شد:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System

Public Class impot
    'داده‌های مورد نیاز برای محاسبه مالیات
    'از یک منبع خارجی تأمین می‌شود
    Private limites(), coeffR(), coeffN() As Decimal

    ' تولیدکننده
    Public Sub New(ByVal LIMITES() As Decimal, ByVal COEFFR() As Decimal, ByVal COEFFN() As Decimal)
        ' ما بررسی می‌کنیم که سه جدول اندازه یکسانی داشته باشند
        Dim OK As Boolean = LIMITES.Length = COEFFR.Length And LIMITES.Length = COEFFN.Length
        If Not OK Then
            Throw New Exception("Les 3 tableaux fournis n'ont pas la même taille(" & LIMITES.Length & "," & COEFFR.Length & "," & COEFFN.Length & ")")
        End If
        'همه‌چیز خوب است
        Me.limites = LIMITES
        Me.coeffR = COEFFR
        Me.coeffN = COEFFN
    End Sub

    'محاسبه مالیات
    Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
        ' تعداد سهام را محاسبه کنید
        Dim nbParts As Decimal
        If marié Then
            nbParts = CDec(nbEnfants) / 2 + 2
        Else
            nbParts = CDec(nbEnfants) / 2 + 1
        End If
        If nbEnfants >= 3 Then
            nbParts += 0.5D
        End If
        'محاسبه درآمد مشمول مالیات و ضریب خانوادگی
        Dim revenu As Decimal = 0.72D * salaire
        Dim QF As Decimal = revenu / nbParts
        'محاسبه مالیات
        limites((limites.Length - 1)) = QF + 1
        Dim i As Integer = 0
        While QF > limites(i)
            i += 1
        End While
        Return CLng(revenu * coeffR(i) - nbParts * coeffN(i))
    End Function
End Class

یک شیء مالیات با داده‌های مورد نیاز برای محاسبه مالیات یک مؤدی ایجاد می‌شود. این بخش ثابت شیء است. پس از ایجاد این شیء، می‌توان متد **calculate** آن را بارها برای محاسبه مالیات مؤدی بر اساس وضعیت تأهل (متأهل یا مجرد)، تعداد فرزندان و حقوق سالانه‌اش فراخوانی کرد. یک برنامه آزمایشی ممکن است به این شکل باشد:


' گزینه‌ها
Option Strict On
Option Explicit On 

' فضاهای نام
Imports System
Imports Microsoft.VisualBasic

Module test
    Sub Main()
        ' برنامه تعاملی محاسبه مالیات
        'کاربر سه مورد داده را از طریق صفحه‌کلید وارد می‌کند: متأهل nbEnfants حقوق
        ' سپس برنامه مالیات قابل پرداخت را نمایش می‌دهد
        Const syntaxe As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"

        ' جدول‌های داده‌ای مورد نیاز برای محاسبه مالیات
        Dim limites() As Decimal = {12620D, 13190D, 15640D, 24740D, 31810D, 39970D, 48360D, 55790D, 92970D, 127860D, 151250D, 172040D, 195000D, 0D}
        Dim coeffR() As Decimal = {0D, 0.05D, 0.1D, 0.15D, 0.2D, 0.25D, 0.3D, 0.35D, 0.4D, 0.45D, 0.5D, 0.55D, 0.6D, 0.65D}
        Dim coeffN() As Decimal = {0D, 631D, 1290.5D, 2072.5D, 3309.5D, 4900D, 6898.5D, 9316.5D, 12106D, 16754.5D, 23147.5D, 30710D, 39312D, 49062D}

        ' ایجاد یک شیء مالیاتی
        Dim objImpôt As impot = Nothing
        Try
            objImpôt = New impot(limites, coeffR, coeffN)
        Catch ex As Exception
            Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
            Environment.Exit(1)
        End Try
        'حلقه بی‌نهایت
        Dim marié As String
        Dim nbEnfants As Integer
        Dim salaire As Long
        While True
            ' پارامترهای محاسبه مالیات درخواست می‌شوند
            Console.Out.Write("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :")
            Dim paramètres As String = Console.In.ReadLine().Trim()
            ' آیا اقدام خاصی لازم است؟
            If paramètres Is Nothing OrElse paramètres = "" Then
                Exit While
            End If
            'بررسی تعداد آرگومان‌ها در خط واردشده
            Dim erreur As Boolean = False
            Dim args As String() = paramètres.Split(Nothing)
            Dim nbParamètres As Integer = args.Length
            If nbParamètres <> 3 Then
                Console.Error.WriteLine(syntaxe)
                erreur = True
            End If
            'بررسی اعتبار پارامترها
            If Not erreur Then
                ' متأهل
                marié = args(0).ToLower()
                If marié <> "o" And marié <> "n" Then
                    erreur = True
                End If
                ' nbEnfants
                Try
                    nbEnfants = Integer.Parse(args(1))
                    If nbEnfants < 0 Then
                        Throw New Exception
                    End If
                Catch
                    erreur = True
                End Try
                ' حقوق
                Try
                    salaire = Integer.Parse(args(2))
                    If salaire < 0 Then
                        Throw New Exception
                    End If
                Catch
                    erreur = True
                End Try
            End If
            ' اگر پارامترها صحیح باشند – مالیات محاسبه می‌شود
            If Not erreur Then
                Console.Out.WriteLine(("impôt=" & objImpôt.calculer(marié = "o", nbEnfants, salaire) & " F"))
            Else
                Console.Error.WriteLine(syntaxe)
            End If
        End While
    End Sub
End Module

در اینجا مثالی از اجرای برنامه قبلی آورده شده است:

dir>dir
12/03/2004  18:20             1 483 impots.vb
12/03/2004  18:21             2 805 test.vb
dos>vbc /t:library impots.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dir>dir
12/03/2004  18:24             4 096 impots.dll
12/03/2004  18:20             1 483 impots.vb
12/03/2004  18:21             2 805 test.vb
dos>vbc /r:impots.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
12/03/2004  18:24             4 096 impots.dll
12/03/2004  18:20             1 483 impots.vb
12/03/2004  18:26             6 144 test.exe
12/03/2004  18:21             2 805 test.vb
dos>test
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :x x x
syntaxe : marié nbEnfants salaire
marié : o pour marié, n pour non marié
nbEnfants : nombre d'enfants
salaire : salaire annuel en F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 200000
impôt=22504 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :