Tür uzantıları

Tür uzantıları (genişletme olarak da adlandırılır), önceden tanımlanmış bir nesne türüne yeni üyeler eklemenize olanak sağlayan bir özellik ailesidir. Üç özellik şunlardır:

  • İç tür uzantıları
  • İsteğe bağlı tür uzantıları
  • Genişletme yöntemleri

Her birinin farklı senaryolarda kullanılabilir ve farklı dezavantajları vardır.

Sözdizimi

// Intrinsic and optional extensions
type typename with
    member self-identifier.member-name =
        body
    ...

// Extension methods
open System.Runtime.CompilerServices

[<Extension>]
type Extensions() =
    [<Extension>]
    static member extension-name (ty: typename, [args]) =
        body
    ...

İç tür uzantıları

İç tür uzantısı, kullanıcı tanımlı bir türü genişleten bir tür uzantısıdır.

İç tür uzantıları, genişlettikleri türle aynı dosyada ve aynı ad alanında veya modülde tanımlanmalıdır. Diğer tüm tanımlar isteğe bağlı tür uzantıları olmalarına neden olur.

İç tür uzantıları bazen işlevselliği tür bildiriminden ayırmanın daha temiz bir yoludur. Aşağıdaki örnekte iç tür uzantısının nasıl tanımlanacağı gösterilmektedir:

namespace Example

type Variant =
    | Num of int
    | Str of string
  
module Variant =
    let print v =
        match v with
        | Num n -> printf "Num %d" n
        | Str s -> printf "Str %s" s

// Add a member to Variant as an extension
type Variant with
    member x.Print() = Variant.print x

Tür uzantısı kullanmak, aşağıdakilerin her birini ayırmanıza olanak tanır:

  • Türün Variant bildirimi
  • Sınıfı "şekline Variant " bağlı olarak yazdırma işlevi
  • Nesne stili .-gösterimi ile yazdırma işlevine erişmenin bir yolu

Bu, üzerinde Varianther şeyi üye olarak tanımlamaya bir alternatiftir. Doğal olarak daha iyi bir yaklaşım olmasa da, bazı durumlarda işlevselliğin daha temiz bir gösterimi olabilir.

İç tür uzantıları, genişlettikleri türün üyeleri olarak derlenir ve tür yansımayla incelendiğinde türünde görünür.

İsteğe bağlı tür uzantıları

İsteğe bağlı tür uzantısı, genişletilmekte olan türün özgün modülü, ad alanı veya derlemesinin dışında görünen bir uzantıdır.

İsteğe bağlı tür uzantıları, kendi tanımlamadığınız bir türü genişletmek için kullanışlıdır. Örneğin:

module Extensions

type IEnumerable<'T> with
    /// Repeat each element of the sequence n times
    member xs.RepeatElements(n: int) =
        seq {
            for x in xs do
                for _ in 1 .. n -> x
        }

Artık modül üzerinde çalıştığınız kapsamda açık olduğu sürece Extensions bu modülün üyesi IEnumerable<T> gibi erişebilirsinizRepeatElements.

İsteğe bağlı uzantılar, yansıma tarafından incelendiğinde genişletilmiş türde görünmez. İsteğe bağlı uzantılar modüllerde olmalıdır ve yalnızca uzantıyı içeren modül açık veya kapsam dışında olduğunda kapsam içindedir.

İsteğe bağlı uzantı üyeleri, nesne örneğinin örtük olarak ilk parametre olarak geçirildiği statik üyelere derlenir. Ancak, nasıl bildirdiklerine göre örnek üyesi veya statik üye gibi davranırlar.

İsteğe bağlı uzantı üyeleri C# veya Visual Basic tüketicilerine de görünmez. Bunlar yalnızca diğer F# kodunda kullanılabilir.

İç ve isteğe bağlı tür uzantılarının genel sınırlaması

Tür değişkeninin kısıtlandığı genel bir türde tür uzantısı bildirmek mümkündür. Gereksinim, uzantı bildirimi kısıtlamasının bildirilen türün kısıtlaması ile eşleşmesidir.

Ancak, bildirilen bir tür ile tür uzantısı arasında kısıtlamalar eşleştiğinde bile, bir kısıtlamanın, tür parametresine bildirilen türden farklı bir gereksinim getiren genişletilmiş bir üyenin gövdesi tarafından çıkarılması mümkündür. Örneğin:

open System.Collections.Generic

// NOT POSSIBLE AND FAILS TO COMPILE!
//
// The member 'Sum' has a different requirement on 'T than the type IEnumerable<'T>
type IEnumerable<'T> with
    member this.Sum() = Seq.sum this

Bu kodun isteğe bağlı bir tür uzantısıyla çalışmasını sağlamak için hiçbir yol yoktur:

  • Olduğu gibi, üyenin türü uzantısının Sum tanımladığı öğeden farklı bir kısıtlaması 'T (static member get_Zero ve static member (+)) vardır.
  • Tür uzantısını ile aynı kısıtlamaya sahip olacak şekilde Sum değiştirmek artık üzerindeki IEnumerable<'T>tanımlı kısıtlamayla eşleşmeyecektir.
  • olarak member inline this.Sum değiştirildiğinde member this.Sum tür kısıtlamalarının eşleşmediğini belirten bir hata oluşur.

İstenen, "uzayda kayan" statik yöntemlerdir ve bir türü genişletiyor gibi sunulabilir. Bu, uzantı yöntemlerinin gerekli olduğu yerdir.

Genişletme yöntemleri

Son olarak, uzantı yöntemleri ("C# stili uzantı üyeleri" olarak da adlandırılır), F# dilinde bir sınıfta statik üye yöntemi olarak bildirilebilir.

Uzantı yöntemleri, tür değişkenini kısıtlayacak genel bir türdeki uzantıları tanımlamak istediğinizde kullanışlıdır. Örneğin:

namespace Extensions

open System.Collections.Generic
open System.Runtime.CompilerServices

[<Extension>]
type IEnumerableExtensions =
    [<Extension>]
    static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs

Bu kod kullanıldığında, açık veya kapsam dahilinde olduğu sürece Extensions üzerinde IEnumerable<T>tanımlanmış gibi Sum görünmesini sağlar.

Uzantının VB.NET kodun kullanılabilmesi için derleme düzeyinde bir ek ExtensionAttribute gereklidir:

module AssemblyInfo
open System.Runtime.CompilerServices
[<assembly:Extension>]
do ()

Diğer açıklamalar

Tür uzantıları da aşağıdaki özniteliklere sahiptir:

  • Erişilebilen herhangi bir tür genişletilebilir.
  • İç ve isteğe bağlı tür uzantıları yalnızca yöntemleri değil, herhangi bir üye türünü tanımlayabilir. Bu nedenle, örneğin uzantı özellikleri de mümkündür.
  • self-identifier Söz dizimindeki belirteç, sıradan üyeler gibi çağrılan türün örneğini temsil eder.
  • Genişletilmiş üyeler statik veya örnek üyeleri olabilir.
  • Tür uzantısındaki tür değişkenleri, bildirilen türün kısıtlamalarıyla eşleşmelidir.

Tür uzantıları için aşağıdaki sınırlamalar da vardır:

  • Tür uzantıları sanal veya soyut yöntemleri desteklemez.
  • Tür uzantıları, genişletme olarak geçersiz kılma yöntemlerini desteklemez.
  • Tür uzantıları Statik Olarak Çözümlenen Tür Parametrelerini desteklemez.
  • İsteğe bağlı Tür uzantıları, oluşturucuları genişletme olarak desteklemez.
  • Tür uzantıları tür kısaltmalarında tanımlanamaz.
  • Tür uzantıları için byref<'T> geçerli değildir (ancak bildirilebilirler).
  • Tür uzantıları öznitelikler için geçerli değildir (ancak bildirilebilirler).
  • Aynı ada sahip diğer yöntemleri aşırı yükleyen uzantılar tanımlayabilirsiniz, ancak belirsiz bir çağrı varsa F# derleyicisi uzantı olmayan yöntemleri tercih eder.

Son olarak, bir tür için birden çok iç tür uzantısı varsa, tüm üyelerin benzersiz olması gerekir. İsteğe bağlı tür uzantıları için, aynı türdeki farklı tür uzantılarındaki üyeler aynı adlara sahip olabilir. Belirsizlik hataları yalnızca istemci kodu aynı üye adlarını tanımlayan iki farklı kapsam açtığında oluşur.

Ayrıca bkz.