*با توجه به این موضوع که فرض است دوستان با مفاهیم شی گرایی آشنایی دارند از توضیح و تشریح این مفاهیم خودداری میکنم.
Classes
کلاس چارچوبی از اشیا است برای نگهداری خواص(Properties) و رفتار ها(Methods) و رخدادها(Events). کلاس پایه ایترین مفهوم در برنامه نویسی شی گراست. ساختار کلی تعربف کلاس در #F به صورت زیر است:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] = [ class ] [ inherit base-type-name(base-constructor-args) ] [ let-bindings ] [ do-bindings ] member-list ... [ end ] type [access-modifier] type-name1 ... and [access-modifier] type-name2 ... ...
انواع access-modifier در #F
- public : دسترسی برای تمام فراخوانها امکان پذیر است
- internal : دسترسی برای تمام فراخوان هایی که در همین assembly هستند امکان پذیر است
- private : دسترسی فقط برای فراخوانهای موجود در همین ماژول امکان پذیر است
نکته : protected access modifier در #F پشتیبانی نمیشود.
مثالی از تعریف کلاس:
type Account(number : int, name : string) = class let mutable amount = 0m end
let myAccount = new Account(123456, "Masoud")
برای تعریف خاصیت در #F باید از کلمه کلیدی member استفاده کنید. در مثال بعدی برای کلاس بالا تابع و خاصیت تعریف خواهیم کرد.
type Account(number : int, name: string) = class let mutable amount = 0m member x.Number = number member x.Name= name member x.Amount = amount member x.Deposit(value) = amount <- amount + value member x.Withdraw(value) = amount <- amount - value end
open System type Account(number : int, name: string) = class let mutable amount = 0m member x.Number = number member x.Name= name member x.Amount = amount member x.Deposit(value) = amount <- amount + value member x.Withdraw(value) = amount <- amount - value end
let masoud= new Account(12345, "Masoud") let saeed = new Account(67890, "Saeed") let transfer amount (source : Account) (target : Account) = source.Withdraw amount target.Deposit amount let printAccount (x : Account) = printfn "x.Number: %i, x.Name: %s, x.Amount: %M" x.Number x.Name x.Amount let main() = let printAccounts() = [masoud; saeed] |> Seq.iter printAccount printfn "\nInializing account" homer.Deposit 50M marge.Deposit 100M printAccounts() printfn "\nTransferring $30 from Masoud to Saeed" transfer 30M masoud saeed
printAccounts() printfn "\nTransferring $75 from Saeed to Masoud" transfer 75M saeed masoud printAccounts() main()
در #F زمانی که قصد داشته باشیم در بعد از وهله سازی از کلاس و فراخوانی سازنده، عملیات خاصی انجام شود(مثل انجام برخی عملیات متداول در سازندههای کلاسهای دات نت) باید از کلمه کلیدی do به همراه یک بلاک از کد استفاده کنیم.
open System open System.Net type Stock(symbol : string) = class let mutable _symbol = String.Empty do //کد مورد نظر در این جا نوشته میشود end
open System type MyType(a:int, b:int) as this = inherit Object() let x = 2*a let y = 2*b do printfn "Initializing object %d %d %d %d %d %d" a b x y (this.Prop1) (this.Prop2) static do printfn "Initializing MyType." member this.Prop1 = 4*x member this.Prop2 = 4*y override this.ToString() = System.String.Format("{0} {1}", this.Prop1, this.Prop2) let obj1 = new MyType(1, 2)
خروجی مثال بالا:
Initializing MyType. Initializing object 1 2 2 4 8 16
برای تعریف خواص به صورت استاتیک مانند #C از کلمه کلیدی static استفاده کنید.مثالی در این زمینه:
type SomeClass(prop : int) = class member x.Prop = prop static member SomeStaticMethod = "This is a static method" end
let instance = new SomeClass(5);; instance.SomeStaticMethod;; output: stdin(81,1): error FS0191: property 'SomeStaticMethod' is static.
SomeClass.SomeStaticMethod;; (* invoking static method *)
همانند #C و سایر زبانهای دات نت امکان تعریف متدهای get و set برای خاصیتهای یک کلاس وجود دارد.
ساختار کلی:
member alias.PropertyName with get() = some-value and set(value) = some-assignment
type MyClass() = class let mutable num = 0 member x.Num with get() = num and set(value) = num <- value end;;
public int Num { get{return num;} set{num=value;} }
type MyClass() = class let mutable num = 0 member x.Num with get() = num and set(value) = if value > 10 || value < 0 then raise (new Exception("Values must be between 0 and 10")) else num <- value end
Interface ها
اینترفیس به تمامی خواص و توابع عمومی اشئایی که آن را پیاده سازی کرده اند اشاره میکند. (توضیحات بیشتر (^ ) و (^ ))ساختار کلی برای تعریف آن به صورت زیر است:
type type-name = interface inherits-decl member-defns end
type IPrintable = abstract member Print : unit -> unit
نکته: در هنگام تعریف توابع و خاصیت در interfaceها باید از کلمه abstract استفاده کنیم. هر کلاسی که از یک یا چند تا اینترفیس ارث ببرد باید تمام خواص و توابع اینتریسها را پیاده سازی کند. در مثال بعدی کلاس SomeClass1 اینترفیس بالا را پیاده سازی میکند. دقت کنید که کلمه this توسط من به عنوان اشاره گر به اشیای کلاس تعیین شده و شما میتونید از هر کلمه یا حرف دیگری استفاده کنید.
type SomeClass1(x: int, y: float) = interface IPrintable with member this.Print() = printfn "%d %f" x y
روش نادرست:
let instance = new SomeClass1(10,20) instance.Print//فراخوانی این متد باعث ایجاد خطای کامپایلری میشود.
let instance = new SomeClass1(10,20) let instanceCast = instance :> IPrintable// استفاده از (<:) برای عملیات تبدیل کلاس به اینترفیس instanceCast.Print
در مثال بعدی کلاسی خواهیم داشت که از سه اینترفیس ارث میبرد. در نتیجه باید تمام متدهای هر سه اینترفیس را پیاده سازی کند.
type Interface1 = abstract member Method1 : int -> int type Interface2 = abstract member Method2 : int -> int type Interface3 = inherit Interface1 inherit Interface2 abstract member Method3 : int -> int type MyClass() = interface Interface3 with member this.Method1(n) = 2 * n member this.Method2(n) = n + 100 member this.Method3(n) = n / 10
let instance = new MyClass() let instanceToCast = instance :> Interface3 instanceToCast.Method3 10
#F از کلاسهای abstract هم پشتیبانی میکند. اگر با کلاسهای abstract در #C آشنایی ندارید میتونید مطالب مورد نظر رو در (^ ) و (^ ) مطالعه کنید. به صورت خلاصه کلاسهای abstract به عنوان کلاسهای پایه در برنامه نویسی شی گرا استفاده میشوند. این کلاسها دارای خواص و متدهای پیاده سازی شده و نشده هستند. خواص و متد هایی که در کلاس پایه abstract پیاده سازی نشده اند باید توسط کلاس هایی که از این کلاس پایه ارث میبرند حتما پیاده سازی شوند.
ساختار کلی تعریف کلاسهای abstract:
[<AbstractClass>] type [ accessibility-modifier ] abstract-class-name = [ inherit base-class-or-interface-name ] [ abstract-member-declarations-and-member-definitions ] abstract member member-name : type-signature
[<AbstractClass>] type Shape(x0 : float, y0 : float) = let mutable x, y = x0, y0 let mutable rotAngle = 0.0 abstract Area : float with get abstract Perimeter : float with get abstract Name : string with get
#1 کلاس اول
type Square(x, y,SideLength) = inherit Shape(x, y)
override this.Area = this.SideLength * this.SideLength override this.Perimeter = this.SideLength * 4. override this.Name = "Square"
type Circle(x, y, radius) = inherit Shape(x, y)
let PI = 3.141592654 member this.Radius = radius override this.Area = PI * this.Radius * this.Radius override this.Perimeter = 2. * PI * this.Radius
structureها در #F دقیقا معال struct در #C هستند. توضیحات بیشتر درباره struct در #C (^ ) و (^ )). اما به طور خلاصه باید ذکر کنم که strucureها تقریبا دارای مفهوم کلاس هستند با اندکی تفاوت که شامل موارد زیر است:
- structureها از نوع مقداری هستند و این بدین معنی است مستقیما درون پشته ذخیره میشوند.
- ارجاع به structureها از نوع ارجاع با مقدار است بر خلاف کلاسها که از نوع ارجاع به منبع هستند.(^ )
- structureها دارای خواص ارث بری نیستند.
- عموما از structure برای ذخیره مجموعه ای از دادهها با حجم و اندازه کم استفاده میشود.
ساختار کلی تعریف structure
[ attributes ] type [accessibility-modifier] type-name = struct type-definition-elements end //یا به صورت زیر [ attributes ] [<StructAttribute>] type [accessibility-modifier] type-name = type-definition-elements
type Point3D = struct val x: float val y: float val z: float end
type Point2D = struct val X: float val Y: float new(x: float, y: float) = { X = x; Y = y } end
در پایان یک مثال مشترک رو در #C و #F پیاده سازی میکنیم: