Typy ogólne

Wartości funkcji języka F#, metody, właściwości i typy agregujące, takie jak klasy, rekordy i dyskryminowane związki, mogą być ogólne. Konstrukcje ogólne zawierają co najmniej jeden parametr typu, który jest zwykle dostarczany przez użytkownika konstrukcji ogólnej. Funkcje ogólne i typy umożliwiają pisanie kodu, który działa z różnymi typami bez powtarzania kodu dla każdego typu. Tworzenie kodu ogólnego może być proste w języku F#, ponieważ często kod jest niejawnie wnioskowany jako ogólny przez wnioskowanie typu kompilatora i automatyczne mechanizmy uogólniania.

Składnia

// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body

// Explicitly generic method.
[ static ] member object-identifier.method-name<type-parameters> parameter-list [ return-type ] =
method-body

// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition

Uwagi

Deklaracja jawnie ogólnej funkcji lub typu jest podobna do funkcji lub typu innego niż ogólny, z wyjątkiem specyfikacji (i użycia) parametrów typu, w nawiasach kątowych po nazwie funkcji lub typu.

Deklaracje są często niejawnie ogólne. Jeśli nie określisz w pełni typu każdego parametru używanego do tworzenia funkcji lub typu, kompilator próbuje wywnioskować typ każdego parametru, wartości i zmiennej z zapisanego kodu. Aby uzyskać więcej informacji, zobacz Wnioskowanie typu. Jeśli kod typu lub funkcji nie ogranicza typów parametrów, funkcja lub typ są niejawnie ogólne. Ten proces nosi nazwę automatycznej uogólniania. Istnieją pewne ograniczenia dotyczące automatycznej uogólniania. Jeśli na przykład kompilator języka F# nie może wywnioskować typów dla konstrukcji ogólnej, kompilator zgłasza błąd, który odwołuje się do ograniczenia o nazwie ograniczenie wartości. W takim przypadku może być konieczne dodanie adnotacji typu. Aby uzyskać więcej informacji na temat automatycznej uogólniania i ograniczenia wartości oraz sposobu zmiany kodu w celu rozwiązania problemu, zobacz Automatyczne uogólnienie.

W poprzedniej składni parametrów type-parameters to rozdzielona przecinkami lista parametrów reprezentujących nieznane typy, z których każdy zaczyna się od pojedynczego cudzysłowu, opcjonalnie z klauzulą ograniczenia, która dodatkowo ogranicza typy, które mogą być używane dla tego parametru typu. Aby uzyskać składnię klauzul ograniczeń różnych rodzajów i innych informacji o ograniczeniach, zobacz Ograniczenia.

Definicja typu w składni jest taka sama jak definicja typu dla typu innego niż ogólny. Zawiera on parametry konstruktora dla typu klasy, klauzuli opcjonalnej as , równego symbolu, pól rekordu, klauzuli, inherit opcji dyskryminowanego związku let i do powiązań, definicji składowych i wszystkich innych dozwolonych w definicji typu innego niż ogólny.

Inne elementy składni są takie same jak w przypadku funkcji i typów innych niż ogólne. Na przykład identyfikator obiektu jest identyfikatorem reprezentującym sam obiekt zawierający.

Właściwości, pola i konstruktory nie mogą być bardziej ogólne niż typ otaczający. Ponadto wartości w module nie mogą być ogólne.

Niejawnie ogólne konstrukcje

Gdy kompilator języka F# wywnioskuje typy w kodzie, automatycznie traktuje dowolną funkcję, która może być ogólna. Jeśli jawnie określisz typ, taki jak typ parametru, uniemożliwisz automatyczne uogólnienie.

W poniższym przykładzie kodu jest ogólny, makeList mimo że ani nie są jawnie deklarowane jako ogólne, ani parametry.

let makeList a b =
    [a; b]

Podpis funkcji jest wnioskowany jako 'a -> 'a -> 'a list. Należy pamiętać, że a w b tym przykładzie wywnioskowane są te same typy. Jest to spowodowane tym, że są one zawarte na liście razem, a wszystkie elementy listy muszą być tego samego typu.

Można również utworzyć funkcję ogólną przy użyciu składni pojedynczego cudzysłowu w adnotacji typu, aby wskazać, że typ parametru jest ogólnym parametrem typu. W poniższym kodzie jest ogólny, function1 ponieważ jego parametry są deklarowane w ten sposób jako parametry typu.

let function1 (x: 'a) (y: 'a) =
    printfn "%A %A" x y

Jawne konstrukcje ogólne

Można również utworzyć funkcję ogólną, jawnie deklarując parametry typu w nawiasach kątowych (<type-parameter>). Ilustruje to poniższy kod.

let function2<'T> (x: 'T) (y: 'T) =
    printfn "%A, %A" x y

Korzystanie z konstrukcji ogólnych

W przypadku używania funkcji ogólnych lub metod może nie być konieczne określenie argumentów typu. Kompilator używa wnioskowania typu, aby wywnioskować odpowiednie argumenty typu. Jeśli nadal istnieje niejednoznaczność, można podać argumenty typu w nawiasach kątowych, oddzielając wiele argumentów typów przecinkami.

Poniższy kod przedstawia użycie funkcji zdefiniowanych w poprzednich sekcjach.

// In this case, the type argument is inferred to be int.
function1 10 20
// In this case, the type argument is float.
function1 10.0 20.0
// Type arguments can be specified, but should only be specified
// if the type parameters are declared explicitly. If specified,
// they have an effect on type inference, so in this example,
// a and b are inferred to have type int.
let function3 a b =
    // The compiler reports a warning:
    function1<int> a b
    // No warning.
    function2<int> a b

Uwaga

Istnieją dwa sposoby odwoływania się do typu ogólnego według nazwy. Na przykład list<int> i int list są dwa sposoby odwoływania się do typu list ogólnego, który ma jeden argument inttypu . Ta ostatnia forma jest zwykle używana tylko z wbudowanymi typami języka F#, takimi jak list i option. Jeśli istnieje wiele argumentów typu, zwykle używasz składni Dictionary<int, string> , ale można również użyć składni (int, string) Dictionary.

Symbole wieloznaczne jako argumenty typu

Aby określić, że argument typu powinien zostać wywnioskowany przez kompilator, możesz użyć znaku podkreślenia lub symbolu wieloznacznego (_), zamiast nazwanego argumentu typu. Jest to pokazane w poniższym kodzie.

let printSequence (sequence1: Collections.seq<_>) =
   Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1

Ograniczenia w typach ogólnych i funkcjach

W definicji typu ogólnego lub funkcji można użyć tylko tych konstrukcji, które są znane jako dostępne w parametrze typu ogólnego. Jest to wymagane do włączenia weryfikacji wywołań funkcji i metod w czasie kompilacji. Jeśli jawnie zadeklarujesz parametry typu, możesz zastosować jawne ograniczenie do ogólnego parametru typu, aby powiadomić kompilator, że niektóre metody i funkcje są dostępne. Jeśli jednak zezwolisz kompilatorowi języka F# na wnioskowanie typów parametrów ogólnych, określi odpowiednie ograniczenia. Aby uzyskać więcej informacji, zobacz Ograniczenia.

Statycznie rozwiązywane parametry typu

Istnieją dwa rodzaje parametrów typu, które mogą być używane w programach języka F#. Pierwszy to ogólne parametry typu rodzaju opisanego w poprzednich sekcjach. Ten pierwszy rodzaj parametru typu jest odpowiednikiem ogólnych parametrów typu, które są używane w językach, takich jak Visual Basic i C#. Inny rodzaj parametru typu jest specyficzny dla języka F# i jest określany jako statycznie rozpoznany parametr typu. Aby uzyskać informacje o tych konstrukcjach, zobacz Statycznie rozwiązane parametry typu.

Przykłady

// A generic function.
// In this example, the generic type parameter 'a makes function3 generic.
let function3 (x : 'a) (y : 'a) =
    printf "%A %A" x y

// A generic record, with the type parameter in angle brackets.
type GR<'a> =
    {
        Field1: 'a;
        Field2: 'a;
    }

// A generic class.
type C<'a>(a : 'a, b : 'a) =
    let z = a
    let y = b
    member this.GenericMethod(x : 'a) =
        printfn "%A %A %A" x y z

// A generic discriminated union.
type U<'a> =
    | Choice1 of 'a
    | Choice2 of 'a * 'a

type Test() =
    // A generic member
    member this.Function1<'a>(x, y) =
        printfn "%A, %A" x y

    // A generic abstract method.
    abstract abstractMethod<'a, 'b> : 'a * 'b -> unit
    override this.abstractMethod<'a, 'b>(x:'a, y:'b) =
         printfn "%A, %A" x y

Zobacz też