Referencia de PerlNET 


F. Javier García Castellano
Web: http://geneura.ugr.es/~javi, Mail: fjgc@decsai.ugr.es
Crear componentes .NETInicioEnlaces

(C) GeNeura Team
Web: http://geneura.ugr.es, Mail: tutti@geneura.ugr.es

1.- Tipos implementados

PerlNET, además de permitirnos usar objetos de .NET, nos permite implementar y extender los tipos de .NET. Como sabemos en Perl de los tipos se preocupa el intérprete, es decir, es débilmente tipificado, mientras que el entorno de trabajo de .NET es fuertemente tipificado. Esto significa que necesitamos añadir un definición de interfaz a nuestra implementación en código Perl. Dicha definición de interfaz se incluye en bloques iguales a los de la documentación POD, dentro del código fuente del fichero.

PerlNET distingue entre tipos de Perl puros, tipos de .NET y tipos "mixtos".

1.1.- Tipos de Perl

Un tipo de Perl puro utiliza una referencia de Perl bendecida (blessed) en la variable $self.

Para implementar que un componente de .NET pueda usar un interfaz un tipo de Perl puro, se tiene que incluir el siguiente pseudo atributo en la definición del interfaz:

=for interface
     [interface: pure]
=cut

Un tipo puro de Perl no puede heredar de un tipo de .NET y no puede implementar campos (fields) o métodos virtuales. Todos las propiedades que acceden al objeto $self se interpretan como accesos al array asociativo del objeto.

Los tipos puros de Perl son los más indicados para utilizar módulos de Perl dentro de .NET, y para nuevos componentes de Perl que también serán usados en el entorno de trabajo de .NET

1.2.- Tipos de .NET

Un tipo de .NET es tipo de .NET normal, que utiliza el parámetro $this que representa el objeto de .NET. Esto significa que no hay ninguna variable escalar bendecida (blessed) en el componente; todo se tiene que guardar en la parte visible del objeto .NET.

1.3.- Tipos Mixtos

Un tipo mixto es básicamente lo mismo que un tipo de .NET, excepto que para todo contructor, método o método acceso a las propiedades, recive dos referencias: $this y $self. Se diseña un interfaz como "mixed" (mixto) con el siguiente pseudo atributo:

=for interface
     [interface: mixed]
=cut

El objeto se presenta por la referencia $this; la variable $slef referencia a un array asocitivo bendecido que permite la objeto manternet las referencias de Perl como datos instanciados. Todas las referencias al mismo objeto deben hacerse aún a través de $this. La referencia $self es dada implicitamente por PerlNET; no se especifica en el interfaz. Por ejemplo:

=for interface
     void Foo(str param);
=cut
sub Foo {
    my($this,$self,$param) = @_;
    $self->{param} = $param;
    $this->Bar();
}

Los tipos mixtos, en los demás aspectos, actúan exactamente como los tipos de .NET. Cuando la referencia this distingue entre tipos de Perl y tipos de .NET, se tratan los tipos mixtos como tipos de .NET.

1.3.-Herencia de tipos

Los componentes de PerlNET pueden heredar de otros tipos de .NET, incluyendo los implementados por PerlNET. .NET solo permite herencia simple. La clase base para un componente de PerlNET se especifica mediante un pseudo atributo:

=for interface
     [extends: BaseClass]
=cut

2.- Interfaces

Los componentes de PerlNET se pueden implementar como interfaces de .NET definidos externamente. La implementación de estos interfaces se tienen que listar como pseudo atributos. Por ejemplo:

=for interface
     [implements: ICollection, IEnumerable]
=cut

Aún así es necesario tener una lista explícita de prototipos para todos los métodos implementados. Este requerimiento podría cambiar en un versión futura de PerlNET. Este versión de PerlNET no permite definir nuevos interfaces de .NET

3.- Espacios de nombres (Namespaces)

3.1.-Crear tipos dentro de un espacio de nombres

El entorno de trabajo de .NET utiliza espacios de nombres (namespaces) para organizar los tipos. PerlNet utiliza el nombre del objeto (package) para deducir automaticamente el espacio de nombres de un tipo. Si se implementa el componente Mi::Modulo::Ejemplo, PerlNET creará automaticamente el tipo Ejemplo en el espacio de nombres Mi.Modulo

3.2.-Usar espacios de nombres para buscar tipos

PerlNET proprciona la palabra reservada namespace para especificar espacios de nombres donde buscar aquellos tipos que no estén cualificados. Por ejemplo:

use namespace "System.Text";

Esto nos permite crear después un objeto System.Text.StringBuilder escribiendo:

my $str = StringBuilder->new("Initial string");

en lugar de tener que escribir:

my $str = System::Text::StringBuilder->new("Initial string");

El nombre resevado namespace tiene efecto tanto en el momento de la compilación (especificación del interfaz) como al ejecutarlo.

4.- Constructores

Los constructores devuelve una nueva instancia de un objeto.

4.1.- Llamar a un constructor

El constructor es un método de clase llamado new(). Los constructores pueden aceptar parámetros.

Type->new(@args)

In case Type is also used as a Perl package, it is always possible to invoke the constructor using the ctor() helper function:

PerlNET::ctor("type", @args)

4.2.- Definición de constructores

En la definición del interfaz, los constructores se definen como métodos estáticos con el mismo nombre que el Tipo en cuestión:

=for interface
     static Type();
     static Type(string arg);
=cut

No es necesario especificar el tipo devuelto; todos los constructores deben devolver un objeto del tipo implementado o lanzar una excepción. Es posible sobrecargar la llamada del constructor proporcionando multiples prototipos. Todos los constructores acabarán por llamar al mismo método de Perl (new); se mirará el array @_ para ver el número de parámetros que se han pasado.

Los constructores se implementan de forma diferente para tipos puros de Perl y tipos de .NET.

4.2.- Tipos de Perl

Para los tipos puros de Perl, el constructor necesita ser llamado new. El primer argumento del constructor será el nombre del Package (del objeto) y el contructor deber devolver una referencia a un objeto bendecido (blessed) dentro de ese package.

sub new {
    my $this=shift; 
    my $class = ref($this) || $this; 
    bless $self, $class; 
    return ($self); 
}

4.3.- Tipos .NET

Para los tipos de .NET, el constructor debe tener el mismo nombre que el tipo. El primer argumento del constructor es el puntero $this al objeto. Es posible invocar métodos de instancia adicionales a través de este puntero durante el momento de su construcción. El constructor es llamado en un contexto void, es decir, no se espera que devuelva nada.

sub Type {
    my($this, @args) = @_;
    $this->Init(@args);
}

4.4.- Constructor por defecto

Si no quieres definir un constructor para tu tipo, PerlNET automaticamente proporcionará un constructor por defecto, que no toma argumentos y que sólo resevará espacio para el objeto.

4.5.- Constructores de clase

Ls contructores de clase se invocan antes de que el tipo se instacie por primera vez. No hay ninguna sintaxis especial para definir un constructor en Perl. La implementación del archivo fuente de Perl del componente se compila y ejecuta como parte del proceso de construcción de la clase.

5.- Métodos

5.1.- Llamando a los Métodos

Los métodos en PerlNET se llaman igual que los métodos de Perl.

$obj->Method(@args);

Los tipos de los argumentos deben corresponder exactamente con los del método. Esto permite a PerlNET seleccionar automaticamente el mejor método sobrecargado en tiempo de ejecución. Por ejemplo, si el método necesita un parámetro entero, no puedes pasarle un flotante. Debido a que Perl convierte automaticamente enteros a flotantes cuando son usados en contexto aritmético, va a ser necesario escribir:

$obj->Method(int($var));

en lugar de sólo:

$obj->Method($var);

Igualmente, los valores booleanos deben ser pasados explicitamente como booleanos, no como enteros o cadenas. PerlNET contiene las constantes booleanas PerlNET::true y PerlNET::false, además de la función PerlNET::bool() que convierte booleanos de Perl en booleanos de .NET

$obj->Method( PerlNET::bool($a == $b) );

5.2.- Métodos Estáticos

Los métodos estáticos (o métodos de clase) se pueden llamar usando la función PerlNET::call():

PerlNET::call("Type.Method", @args);

También es posible obligar a PerlNET para que convierta automaticamente la utilización de los métodos de clase usados en Perl a los usados en .NET

use PerlNET qw(AUTOCALL);

Entonces, la llamada anterior se puede reescribir como:

asType->Method(@args);

5.3.- Definición de Métodos

Todos los métodos deden ser declarados en la definición del interfaz. Una declaración de un método consiste en una lista de modificadores del métodos, seguido por tipo que devuelve, seguido por el nombre del método, seguido de una declaración de la lista de parámetros. Por ejemplo:

=for interface
     static int StaticMethod();
     protected virtual void VirtualMethod(int Arg1, int Arg2);
     override str BaseMethod();
     void OverloadedMethod(int arg);
     void OverloadedMethod(str arg);
=cut

Los modificadores de los métodos incluyen modificadores de acceso: public, protected, private o internal. Son mutuamente exclusivos, excepto para protected internal. El acceso por defecto es public.

Otro conjunto de modificadores mutuamente exclusivo son static, virtual y override. static indica que es un método de clase, virtual que el método puede ser reemplazado en una clase derivada, y override que estamos ignorando un método que ya se ha definido en nuestra clase base. Además la característica "platform invoke" (t/invoke) usa la combinación de static external para indicar una declaración hacia una función sin manejar en Win32 en una DLL externa.

La declaración de parámetro es una lista de tipos separado por comas y nombres de parametros encerrados entre parentesis.

5.4.- Sobrecarga

Todos los métodos sobrecargados llamarán a un sólo metodo de Perl con el mismo nombre. El método de Perl inspeccionará los tipos de los argumentos en @_ para determinar que llamada se ha realizado para invocar al método. No está permitido sobrecargar un método puro en el tipo de retorno.

5.5.- Contexto de listas

Normalmente, todos los método se pueden llamar en un contexto escalar, es decir, que devuelva un escalar. Si el método ha sido declarado que debe devolver un array, entonces el método de Perl que espera devolver o una referencia de Perl conteniendo el array con elementos del tipo correcto o un array de .NET.

Para interfaces de Perl puros, especialmente cuando se usan métodos enmascarados para ser usados en .NET, es útil permitir invocar a un método en contexto de listas y que automaticamente transforme la lista devuelta en un array de .NET con el tipo correcto. Esto se puede hacer en el interfaz con la palabra reservada wantarray!

=for interface
     static wantarray! str[] SomeWords();
=cut
sub SomeWords {
    return qw(a list of some words);
}

6.- Variables de Instancia (Propiedades)

El entorno de .NET utiliza propiedades con tipo. Se acceden a estas propiedades mediante métodos de lectura (getter) y de modificación (setter).

6.1.- Métodos de acceso

Los propiedades se acceden usando la sintaxis de una referencia a una tabla hash:

$obj->{Property} = $value;
print "Property is $obj->{Property}\n";

No está permitido tener una propiedad y un método con el mismo nombre. Por lo tanto, es también factible recuperar el valor de una propiedad usando la siguiente sintaxis en la llamada:

printf "Property is %s\n", $obj->Property;

Sin embargo, para modificar el valor de la propiedad se ha de usar siempre la sintasix de una referencia a una tabla hash.

Se puede acceder a las propiedades estáticas usando las funciones de ayuda PerlNET::get y PerlNET::set:

my $prop = PerlNET::get("MyType.Property");
PerlNET::set("MyType.Property", $value);

6.2.- Definición de Propiedades

Se define una propiedad mediante un mofificador de acceso, seguido del el tipo, seguido del nombre de la propiedad. Los métodos de modificación y lectura (set y get) deben proporcionar los nuevos valores como argumento:

=for interface
     protected str MyProp;
=cut

sub MyProp {
    my($this, $value) = @_;
    unless (defined $value) {
        # return current value
    }
    # set property to $value
}

6.3.- Propiedades con parámetros

Las propiedades, pueden inicialmente, tomar parámetros. No hay una sintaxis especial en PerlNET. Se puede acceder a través del interfaz de la llamada a método: la función de modificación (set) se llama set_NombrePropiedad() y el de lectura (get) es get_NombrePropiedad.

This release of PerlNET does not support the definition of properties with parameters.

7.- Indexados

Mucho tipos tiene el concepto de "indexación por defecto". Esto es sólo un propiedad, tomando (normalmente) un sólo argumento. Los Índices se usan para acceder aquellos elementos de una colección que no tiene, explicitamente, un nombre de propiedad.

7.1.- Acceder a los indexados

PerlNET utiliza la sintaxis de referencias a array para implementar indexados:

print "The second character is $str->[1]\n";

debido a la forma que que Perl funciona internamente, esto sólo funcionara si el indexador espera un parámetro entero. En todos los otros caso debe ser invocado usando la sintaxis de métodos. Normalmente, el nombre de una una propiedad indexada es Item, pero alguno tipos usan un nombre diferente (por ejemplo, System.String y System.Text.StringBuilder utilizan Chars).

El anterior ejemplo reescrito con la sintasix de método quedaría como sigue:

printf "The second character is %s\n", $str->get_Chars(1);

7.2.- Definición de indexados

Esta versión de PerlNET no soporta la definición de propiedades indexadas.

8.- Campos

Los campos son similares a las propiedades. La diferencia es que no se acceden mediante métodos de acceso, sino directamente por la ubicación de memoria

8.1.- Acceso a campos

En Perl se acceden usando la sintaxis de referencias a hash, como si fueran propiedades. No está permitido tener una propiedad y un campo con el mismo nombre. Lo campos nunca toman parámetros.

Se accede a los campos estáticos a través de PerlNET::get() y PerlNET::set(), como si fueran propiedades estáticas. Adicionalmente , los objetos que poseen campos pueden ser accedidos a través de variables del package con el mismo nombre. PerlNET une estas variables globales con el correspondiente campo de .NET:

package MyClass;
=for interface
     static field str Name;
=cut

$Name = "Foo";
# implicitly does the same as
PerlNET::set("MyClass.Name", "Foo");

8.2.- Definición de campos

Los campos se definen como las propiedades. Además, se necesita especificar un modificador de campo con la palabra reservada field::

private field int Count;

9.- Tipos de los valores

PerlNET almacena los valores de los tipos de .NET como objetos en cajas ocultas, manteniendo información de su tipo, en lugar de convertirlos a numeros y enteros de Perl. Esto hace más fácil pasar esos valores a métodos de .NET.

PerlNET también proporciona constructores preconstruidos para los distintos tipos de valores:

use PerlNET qw(char);
# ...
my $ch = char('a');

El constructor/operador de casting PerlNET::char() produce un valor System.char . La siguiente tabla muestra el mapeo entre los nombres PerlNET y los nombres de los tipos de System.* .

PerlNET System Ejemplo
bool Boolean bool(0)
char Char char("\x{263A}")
sbyte SByte sbyte(-128)
short Int16 short(0x7fff)
int Int32 int(42)
long Int64 long("12345")
byte Byte byte(255)
ushort UInt16 ushort(0xffff)
uint UInt32 uint(0)
ulong UInt61 ulong("-1")
float Single float(3.14)
double Double double(1.0)
decimal Decimal decimal("7")

PerlNET convierte automaticamente todos los valores de los tipos en cadenas asi como lo vaya necesitando y además implementa autoincremento y autodecremento de sus operadores, cubriendo todo el rango válido de valores. La conducta "envolvente" (warp-around) es la misma que los tipos subyacentes de .NET:

my $byte = PerlNET::byte(255);
++$byte;

tiene el mismo resultado que:

my $byte = PerlNET::byte(0);

Sin embargo, en las operaciones aritméticas convertira los valores de los tipos de .NET en número enteros o flotantes de Perl. Eso significa que ese resultado no será por más tiempo un tipo de un valor de .NET, y para aquellos que el valor excede los enteros de 32 bits (para los tipos long, ulong y decimal) puede no ser convertido correctamente. Comparaciones numéricas para estos 3 tipos tampoco funcionarán. Se está considerando sobrecargar operaciones aritméticas en una futura versión de PerlNET.

use PerlNET qw(uint);
# ...
my $word = uint(42);
$word += 7;

# need to convert back to System.UInt32 as $word is now just a Perl int
$obj->Method( uint($word) );

Nótese que se debe ignorar la función int si se importa PerlNET::int() en nuestro programa:

use PerlNET qw(int);
# ...

# use CORE::int() to call builtin Perl int() function
my $int = CORE::int($val*1.68);

9.1.- Booleans

PerlNET trata los valores System.Boolean de igual forma que las expresiones booleanas de Perl: En contextos numéricos a 1 o 0 y en contextos de cadenas como "1" o "" (la cadena vacía).

PerlNET también define las constantes PerlNET::true y PerlNET::false, que son iguales a PerlNET::bool(1) y PerlNET::bool(0) respectivamente.

10.- Enumeraciones

Las enumeraciones proporcionan nombres simbólicos a constantes numéricas (enteros).

10.1.- Usar constantes de enumeraciones

Se accede a los miembros de enumeraciones usando la función de apoyo PerlNET::enum() :

my $monday = PerlNET::enum("System.DayOfWeek.Monday");

Todos los objetos de enumeración devolverán sus valores numéricos cuando se usen como números y como representaciónes de texto cuando se usen como cadenas. Las siguientes expresiones son las dos verdad:

$monday eq "Monday"
$monday == 1

10.2.- Definición de tipos enumerados

Esta versión de PerlNET no soporta la definición de tipos enumerados.

11.- Delegaciones

11.1.- Usar delegaciones

Las delegaciones se construyen como cualquier otro objeto de .NET . El primer argumento al constructor es un un puntero a objeto y el segundo es el nombre la función callback:

my $handler = System::EventHandler->new($this, "callback");

Esta versión de PerlNET no soporta delegados en tipos puros de Perl.

11.2.- Definición de delegaciones

Esta versión de PerlNET no soporta la definición de delegaciones.

12.- Excepciones

12.1.- Lanzar una excepción des código Perl

Cuando un método de Perl muere, el mensaje de la excepción se propaga al entorno de .NET y puede ser tratadas allí.

12.2.- Tratamiento de excepciones en código de Perl

Las excepciones lanzadas desde código de .NET puedes cogidas en un bloque eval desde Perl.

13.- Atributos propios (custom attributes)

Esta versión de PerlNET no soporta la definición de atributos propios.

14.- Función Main

Si se proporciona un método estático llamado Main() a tu componente y compilas dicho componente en un fichero .EXE, Entonces, PerlNET hará de esta función tu punto de entrada. Esto es principalemnte útil si necesitas especificar atributos propios para tu función Main, por ejemplo, [STAThread] en WinForms.

15.- Arrays

Los objetos Array se pueden construir usando la sintaxis de [] :

my $a = "System.Int32[]"->new(1 .. 5);

16.- Opciones de la línea de comandos

El compilador de PerlNET, plc, soporta opciones en la línea de comando usando el siguiente formato:

plc [opciones] fichero_fuente

Las opciones soportadas son

-freestanding   (empotra todos los módulos de Perl en assembly)
-nologo         (no muestra los mensaje de copyright)
-out FILENAME   (espcifica el fichero de salida)
-ref ASSEMBLY   (añade referencia ASSEMBLY )
-target winexe  (crea una aplicación con  GUI (WinForms))
-target library (crea un  DLL (en lugar de EXE))
-verbose        (propociona más información)