Compartir vía


Genéricos

Los valores de función, métodos, propiedades y tipos agregados de F# como clases, registros y uniones discriminadas pueden ser genéricos. Las construcciones genéricas contienen al menos un parámetro de tipo, que normalmente proporciona el usuario de la construcción genérica. Las funciones y tipos genéricos permiten escribir código que funcione con una variedad de tipos sin repetir el código para cada tipo. Hacer que el código genérico sea sencillo en F#, ya que a menudo el código se deduce implícitamente para que sea genérico mediante los mecanismos de inferencia de tipos y generalización automática del compilador.

Sintaxis

// 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

Observaciones

La declaración de una función o tipo genérico explícitamente es muy similar a la de una función o tipo no genérico, excepto para la especificación (y el uso) de los parámetros de tipo, entre corchetes angulares después de la función o el nombre de tipo.

Las declaraciones suelen ser genéricas implícitamente. Si no especifica completamente el tipo de cada parámetro que se usa para componer una función o un tipo, el compilador intenta deducir el tipo de cada parámetro, valor y variable del código que escribe. Para obtener más información, vea Inferencia de tipos. Si el código de su tipo o función no restringe los tipos de parámetros, la función o el tipo es implícitamente genérico. Este proceso se denomina generalización automática. Hay algunos límites en la generalización automática. Por ejemplo, si el compilador de F# no puede deducir los tipos de una construcción genérica, el compilador notifica un error que hace referencia a una restricción denominada restricción de valor. En ese caso, es posible que tenga que agregar algunas anotaciones de tipo. Para obtener más información sobre la generalización automática y la restricción de valores, y cómo cambiar el código para solucionar el problema, consulte Generalización automática.

En la sintaxis anterior, los parámetros de tipo son una lista separada por comas de parámetros que representan tipos desconocidos, cada uno de los cuales comienza con una comilla única, opcionalmente con una cláusula de restricción que limita aún más los tipos que se pueden usar para ese parámetro de tipo. Para obtener la sintaxis de las cláusulas de restricción de varios tipos y otra información sobre las restricciones, vea Restricciones.

La definición de tipo de la sintaxis es la misma que la definición de tipo para un tipo no genérico. Incluye los parámetros de constructor de un tipo de clase, una cláusula opcionalas, el símbolo igual, los campos de registro, la inherit cláusula , las opciones de una unión discriminada y do los enlaces, let las definiciones de miembro y cualquier otra cosa permitida en una definición de tipo no genérico.

Los demás elementos de sintaxis son los mismos que los de funciones y tipos no genéricos. Por ejemplo, el identificador de objeto es un identificador que representa el propio objeto contenedor.

Las propiedades, los campos y los constructores no pueden ser más genéricos que el tipo envolvente. Además, los valores de un módulo no pueden ser genéricos.

Construcciones genéricas implícitamente

Cuando el compilador de F# deduce los tipos del código, trata automáticamente cualquier función que pueda ser genérica. Si especifica explícitamente un tipo, como un tipo de parámetro, se impide la generalización automática.

En el ejemplo de código siguiente, makeList es genérico, aunque ni ni sus parámetros se declaren explícitamente como genéricos.

let makeList a b = [ a; b ]

La firma de la función se deduce como 'a -> 'a -> 'a list. Tenga en cuenta que a y b en este ejemplo se deducen para tener el mismo tipo. Esto se debe a que se incluyen en una lista juntas y todos los elementos de una lista deben ser del mismo tipo.

También puede hacer que una función sea genérica mediante la sintaxis de comillas simples en una anotación de tipo para indicar que un tipo de parámetro es un parámetro de tipo genérico. En el código siguiente, function1 es genérico porque sus parámetros se declaran de esta manera, como parámetros de tipo.

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

Construcciones genéricas explícitas

También puede hacer que una función sea genérica declarando explícitamente sus parámetros de tipo entre corchetes angulares (<type-parameter>). El código siguiente ilustra esto.

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

Uso de construcciones genéricas

Al usar funciones o métodos genéricos, es posible que no tenga que especificar los argumentos de tipo. El compilador usa la inferencia de tipos para inferir los argumentos de tipo adecuados. Si todavía hay una ambigüedad, puede proporcionar argumentos de tipo entre corchetes angulares, separando varios argumentos de tipo con comas.

En el código siguiente se muestra el uso de las funciones definidas en las secciones anteriores.

// 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

Nota:

Hay dos maneras de hacer referencia a un tipo genérico por nombre. Por ejemplo, list<int> y int list son dos maneras de hacer referencia a un tipo list genérico que tiene un único argumento intde tipo . Esta última forma solo se usa convencionalmente con tipos de F# integrados, como list y option. Si hay varios argumentos de tipo, normalmente se usa la sintaxis Dictionary<int, string> , pero también puede usar la sintaxis (int, string) Dictionary.

Caracteres comodín como argumentos de tipo

Para especificar que el compilador debe inferir un argumento de tipo, puede usar el carácter de subrayado o el símbolo comodín (_), en lugar de un argumento de tipo con nombre. Esto se muestra en el código siguiente.

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

Restricciones en funciones y tipos genéricos

En una definición de función o tipo genérico, solo puede usar esas construcciones que se sabe que están disponibles en el parámetro de tipo genérico. Esto es necesario para habilitar la comprobación de las llamadas de función y método en tiempo de compilación. Si declara explícitamente los parámetros de tipo, puede aplicar una restricción explícita a un parámetro de tipo genérico para notificar al compilador que hay disponibles determinados métodos y funciones. Sin embargo, si permite que el compilador de F# infiere los tipos de parámetros genéricos, determinará las restricciones adecuadas para usted. Para más información, vea Restricciones.

Parámetros de tipo resueltos estáticamente

Hay dos tipos de parámetros de tipo que se pueden usar en programas de F#. La primera son parámetros de tipo genéricos del tipo descrito en las secciones anteriores. Este primer tipo de parámetro de tipo es equivalente a los parámetros de tipo genérico que se usan en lenguajes como Visual Basic y C#. Otro tipo de parámetro de tipo es específico de F# y se conoce como parámetro de tipo resuelto estáticamente. Para obtener información sobre estas construcciones, vea Parámetros de tipo resueltos estáticamente.

Ejemplos

// 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

Consulte también