|
PerlNET is ActiveState's technology that allows .NET Framework code to access
Perl code running outside Microsoft's .NET framework using the traditional Perl
interpreter.
PerlNET provides the following functionality:
-
Perl code runs at the same speed within .NET as it does outside.
-
All extension modules, including the ones using XS code, are supported.
-
PerlNET code is completely compatible with the standard Perl language,
including the string form of eval and the runtime use of require.
PerlNET features include:
-
Create .NET applications using .NET components.
-
Wrap existing Perl modules into .NET components.
-
Create new .NET components written in Perl.
-
Extend existing .NET components with Perl.
Here's a "Hello World" program in Perl, using the .NET System. The console
class to output the string could look like this:
use namespace "System";
use PerlNET qw(AUTOCALL);
Console->WriteLine("Hello World!");
This program can be compiled from the command line with the plc command:
plc hello.pl
It produces an executable file called hello.exe. The output of this program is:
Hello World!
The following points about this sample should be noted:
-
The use namespace "System"; pragma tells Perl to look up types in the System
namespace. This allows use of the unqualified type Console instead of
System.Console later in the program.
-
The use PerlNET qw(AUTOCALL); pragma instructs Perl to automatically translate
Perl class-method calls into .NET calls.
The combined effect of both directives is that the statement:
Console->WriteLine("Hello World!")
...is treated as a short version of:
PerlNET::call("System.Console.WriteLine", "Hello World!");
PerlNET makes it easy to wrap an existing Perl module by just providing an
additional interface specification for it. The following sample code wraps the
WWW::Babelfish module from CPAN into a .NET component:
package WWW::Babelfish;
=for interface
[interface: pure]
static Babelfish Babelfish();
str translate(str key1, str value1,
str key2, str value2,
str key3, str value3);
wantarray str[] languages();
=cut
require WWW::Babelfish;
This makes WWW::Babelfish available to every other .NET-compliant language.
The following C# program gives an example:
using System;
class main
{
public static void Main(string[] args)
{
string text = "The quick brown fox jumps over the lazy dog";
string[] lang = { "French", "German", "Spanish", "Italian" };
Console.WriteLine("{0}: {1}", "English", text);
using (WWW.Babelfish fish = new WWW.Babelfish()) {
foreach (string dest in lang) {
string trans = fish.translate("source", "English",
"destination", dest,
"text", text);
Console.WriteLine("{0}: {1}", dest, trans);
}
Console.WriteLine();
foreach (string str in fish.languages())
Console.WriteLine("Language: {0}", str);
}
}
}
To run this sample, first install WWW::Babelfish on your system. Then
compile the PerlNET component and the test program with:
plc --target library Babelfish.pl
csc test.cs -r:Babelfish.dll
PerlNet components require either Perl58RT72.dll and Perl58NH72.dll for
ActivePerl 5.6 and 5.8 support, or Perl510RT72.dll and Perl510NH72.dll
for ActivePerl 5.10 support. You will need to distribute these 2 DLLs with your
.NET assemblies.
Perl58NH72.dll and Perl510NH72.dll can be found in C:\Program
Files\ActiveState Perl Dev Kit 7.2\bin (the default PDK 7.2 install location).
Perl58RT72.dll and Perl510RT72.dll are installed in the Global Assembly
Cache (GAC). The directory for the ActivePerl 5.10 runtime library will look
like this:
C:\WINDOWS\assembly\GAC_MSIL\Perl510RT72\7.2.0.22655__cea8284aa6739163\
The following example shows how to create an instance of an object, invoke
methods through it and access its properties:
# Call constructor (with arguments)
my $str = System::Text::StringBuilder->new("Hello");
# Instance-method call
$str->Append(" World!");
# Reading named and indexed property
printf "str=%s length=%d firstchar=%s\n",
$str, $str->{Length}, $str->[0];
# Assigning to an indexed property
$str->{Length} = 11; # Remove trailing '!'
$str->[5] = '-'; # Replace space by dash
printf "str=%s length=%d firstchar=%s\n",
$str, $str->{Length}, $str->[0];
The first statement of the program constructs a StringBuilder object,
initialized to the string "Hello":
my $str = System::Text::StringBuilder->new("Hello");
Since a namespace pragma was not used, a fully qualified type name is required.
To avoid using quotes around the typename, we replaced the dots in
System.Text.StringBuilder with the :: separators.
The type constructor is always called new() and can potentially take
parameters. PerlNET automatically selects the right constructor based on
parameter types.
Calling methods on .NET objects uses exactly the same syntax as calling Perl
methods:
$str->Append(" World!");
The $str object now contains the text "Hello World!". Using $str in a
string context automatically invokes the ToString() method.
Access named properties of an object using the hash reference syntax and
indexed properties through array references:
printf "str=%s length=%d firstchar=%s\n",
$str, $str->{Length}, $str->[0];
This will print:
str=Hello World! length=12 firstchar=H
Named and indexed properties can also be changed by simple assignment. Changing
the Length property truncates the string; the indexed assignment replaces a
single character:
$str->{Length} = 11;
$str->[5] = '-';
This will now print:
str=Hello-World length=11 firstchar=H
"Platform Invoke" provides access to normal unmanaged Win32 functions in
external DLLs. The following sample determines the path of the temp
directory and displays it in a messagebox:
use strict;
use PerlNET qw(call);
use namespace "System.Text";
use namespace "System.Runtime.InteropServices";
=for interface
[DllImport("kernel32.dll")]
static external int GetTempPath(int bufsize, StringBuilder buffer);
[DllImport("user32.dll")]
static external int MessageBox(int h, str m, str c, int type);
=cut
my $path = StringBuilder->new(300);
my $res = call("GetTempPath", 300, $path);
call("MessageBox", 0, $path->ToString(), "@ARGV", 0);
This release of PerlNET does not support callbacks from unmanaged code because
it doesn't support the definition of new Delegates.
The following sample shows how .NET-compliant Perl code can make use of the
Microsoft Winforms function.
package HelloWorldForm;
use strict;
use PerlNET qw(with AUTOCALL);
use namespace "System";
use namespace "System.Windows.Forms";
use namespace "System.Drawing";
=for interface
[extends: Form]
[STAThread]
static void Main();
private field TextBox textBox1;
private void button1_Click(any sender, EventArgs evArgs);
=cut
sub Main {
my $self = HelloWorldForm->new;
Application->Run($self);
}
sub HelloWorldForm {
my $this = shift;
with(my $textBox1 = TextBox->new(),
Text => "Hello Windows Forms World",
Location => Point->new(16, 24),
Size => Size->new(360, 20),
TabIndex => 1);
with(my $button1 = Button->new(),
Text => "Click Me!",
Location => Point->new(256, 64),
Size => Size->new(120, 40),
TabIndex => 2);
$button1->add_Click(EventHandler->new($this, "button1_Click"));
my $height = SystemInformation->CaptionHeight;
with($this,
Text => "Hello Windows Forms World",
AutoScaleBaseSize => Size->new(5, 13),
ClientSize => Size->new(392, 117),
MinimumSize => Size->new(392, int(117 + $height)),
AcceptButton => $button1,
textBox1 => $textBox1);
$this->{Controls}->Add($_) for $textBox1, $button1;
}
sub button1_Click {
my($this, $sender, $evargs) = @_;
MessageBox->Show("Text is: '$this->{textBox1}->{Text}'");
}
PerlNET not only provides access to .NET objects but also supports implementing
and extending .NET types. The .NET Framework is a strongly typed environment,
whereas Perl is a dynamically typed language. It is therefore necessary to
supply an interface definition in addition to the implementation in plain Perl
code. This interface definition is included in POD blocks throughout the
component source file.
PerlNET distinguishes between "pure" Perl types, .NET types and "mixed" types.
A pure Perl type uses a blessed Perl reference as the $self variable.
To designate a component to use a pure Perl interface you have to include the
following pseudo custom attribute in the interface specification:
=for interface
[interface: pure]
=cut
A pure Perl type cannot inherit from a .NET type and cannot implement fields or
virtual methods. All property accesses to the $self object are interpreted
as accessing the blessed hash.
Pure Perl types are best used for wrapping existing Perl modules, and for new
Perl components that will also be used outside the .NET Framework.
A .NET type uses a $this parameter that represents the actual .NET object.
No blessed Perl scalar is available to the component; everything needs to be
stored in the .NET visible part of the object.
A mixed type is basically the same as a .NET type, except that every
constructor, method and property accessor receives both a $this and a
$self pointer. You designate an interface as "mixed" with the following
pseudo custom attribute:
=for interface
[interface: mixed]
=cut
The object is represented by the $this pointer; the $self variable
references a blessed hash that allows the object to keep Perl references as
instance data. All references to the object itself should still be made through
the $this pointer. The $self pointer is implicitly provided by PerlNET;
it is not listed in the interface signature. For example:
=for interface
void Foo(str param);
=cut
sub Foo {
my($this,$self,$param) = @_;
$self->{param} = $param;
$this->Bar();
}
In all other aspects, mixed types act exactly as .NET types. Whenever this
reference distinguishes between Perl types and .NET types, treat mixed types as
.NET types.
PerlNET components can inherit from other .NET types, including ones
implemented by PerlNET. .NET only allows single inheritance. The base class for
a PerlNET component is specified by a pseudo custom attribute:
=for interface
[extends: BaseClass]
=cut
Perl uses reference counting to do garbage collection: whenever the last
reference to an object is removed, the object is collected (the DESTROY method
is called and the memory is freed).
The .NET Framework uses a 'mark and sweep' algorithm to reclaim unreferenced
memory. When it runs out of memory on the heap, it enumerates all objects, and
any object no longer accessible will be reclaimed. Since no reference counting
is being performed, both the time and the order of destruction are
indeterminate.
For managing the lifetime of unmanaged resources encapsulated in managed
objects, .NET defines the IDisposable interface. It is a standardized mechanism
to release resources held by an object once they are no longer being used.
Please refer to the .NET Framework documentation to learn more about the IDisposable interface.
An object implementing IDisposable is typically used like this (in C#):
MyObj obj = new MyObj();
try {
// do something with obj
}
finally {
obj.Dispose();
}
C# provides some syntactic sugar for this usage pattern in form of the using
statement:
using (MyObj obj = new MyObj()) {
// do something with obj
}
PerlNET implements the IDisposable interface when the 'disposable' attribute is
specified:
=for interface
[interface: disposable]
=cut
It can also be combined with the 'pure' or 'mixed' attribute:
=for interface
[interface: mixed, disposable]
=cut
Whenever possible, PerlNET implements the IDisposable paradigm recommended in
the .NET Framework documentation by providing the following three member
functions:
public void Dispose();
protected virtual void Dispose(bool disposing);
protected void virtual Finalize();
When the PerlNET component inherits from a base class that already implements
IDisposable in a different style, PerlNET tries to stay compatible with the
base class implementation.
Calling the Dispose() method on a 'pure' PerlNET object immediately
decrements the reference count of the Perl object. The Destroy() method is
called if it is the last reference. The .NET part of the object continues to
exist, but calling any method (except Dispose()) raises an
ObjectDisposedException.
These types must define a special Dispose() method:
sub DISPOSE {
my($this,$disposing) = @_;
# or ($this,$self,$disposing) for "mixed" types
# ...
}
$disposing is true when called from the <Dispose()> method and false when
called by the finalizer. Do not access any managed objects called from the
finalizer because the other objects may have already been destroyed by the
garbage collector.
Disposable .NET types must not define a Destroy() method. The method is
defined by PerlNET as part of the IDisposable implementation.
PerlNET components can implement .NET interfaces defined in an external
assembly. The implemented interfaces need to be listed in a pseudo custom
attribute. For example:
=for interface
[implements: ICollection, IEnumerable]
=cut
You must list prototypes for all implemented methods explicitly. This
requirement may change in a future release of PerlNET. This release of PerlNET
does not allow you to define new .NET interfaces.
The .NET Framework uses namespaces to organize types. PerlNET uses the Perl
package name to deduce the namespace for a type. If you implement a
My::Package::Sample component, PerlNET automatically creates the type sample
in the My.Package namespace.
PerlNET also provides a namespace pragma to specify namespaces that should be
searched to resolve non-qualified typenames. For example:
use namespace "System.Text";
This lets you later create a System.Text.StringBuilder object by writing:
my $str = StringBuilder->new("Initial string");
...instead of:
my $str = System::Text::StringBuilder->new("Initial string");
The namespace pragma is in effect both at compiletime (interface
specifications) and at runtime.
Constructors return a new instance of a type.
The constructor is called as a class method called new(). Constructors may
take additional arguments:
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)
In the interface definition, constructors are defined as static methods with
the same name as the type itself:
=for interface
static Type();
static Type(string arg);
=cut
It is not necessary to specify a return type; all constructors must return an
object of the implemented type or raise an exception. It is possible to
overload the signature of the constructor by providing multiple prototypes. All
constructors will call the same Perl method; you can inspect @_ to determine
what parameters have been provided.
Constructors are implemented differently for pure Perl vs. .NET types.
For pure Perl types, the constructor needs to be called 'new'. The first
argument to the constructor is the package name, and the constructor must
return a reference to an object blessed into this package.
sub new {
my($package) = @_;
return bless {}, $package;
}
For .NET types, the constructor must have the same name as the type. The first
argument to the constructor is the $this pointer to the already-allocated
object. It is possible to invoke additional instance methods through this
pointer during construction time. The constructor is called in void context; it
is not expected to return anything.
sub Type {
my($this, @args) = @_;
$this->Init(@args);
}
If you do not define a constructor for your type, PerlNET automatically
provides a default constructor that takes no arguments but just allocates the
object.
Class constructors are invoked before the type is instantiated for the first
time. There is no special syntax to define a class constructor in Perl. The
Perl source file implementing the component is compiled and executed as part of
the class construction process.
.NET methods are called just like normal Perl methods:
$obj->Method(@args);
The types of the arguments must match the types of the method signature
exactly. This allows PerlNET to automatically select the correct overloaded
method at runtime. For example, if the method requires an int parameter, you
cannot pass a float. Because Perl automatically translates integers to floats
whenever they are used in an arithmetic context, it may be necessary to write:
$obj->Method(int($var));
...instead of just:
$obj->Method($var);
Similarly, boolean values must be explicitly passed as boolean, not as int
or str. PerlNET contains the boolean constants VPerlNET::true and
PerlNET::false as well as the PerlNET::bool() function that converts a
Perl boolean value into a .NET boolean.
$obj->Method( PerlNET::bool($a == $b) );
Sometimes you may want to pass a null reference as a method parameter. The Perl
undef value is implicitly treated as being of System.Object type. The
PerlNET::null() function provides typed null references to help PerlNET
select the right method signature:
$obj->Method( PerlNET::null("System.String") );
Static methods (aka class methods) can be called using the PerlNET::call()
function:
PerlNET::call("Type.Method", @args);
It is also possible to instruct PerlNET to retry every static method call for
which no Perl method is found. This feature needs to be enabled explicitly
with:
use PerlNET qw(AUTOCALL);
Then the call above can be rewritten as:
Type->Method(@args);
All methods must be declared in the interface definition. A method declaration
consists of a list of method modifiers, followed by the return type, followed
by the method name, followed by the parameter list declaration. For example:
=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
Method modifiers include access modifiers: public, protected, private
or internal. They are mutually exclusive except for protected internal. The
default access is public.
Another set of mutually exclusive modifiers consists of static, virtual
and <override>. static indicates that this is a class method, virtual
that the method can be overridden in a derived class, and override that a
method already defined in the base class is overridden. In addition, the
'platform invoke' feature (P/invoke) uses the combination of static
external to indicate a forwarder declaration to an unmanaged Win32 function in
an external DLL.
The parameter declaration is a comma-separated list of type and parameter names
enclosed in parentheses.
All overloaded methods call the single Perl method of the same name. The Perl
method can inspect the types of the arguments in @_ to determine which
signature was used to invoke the method. It is not allowed to overload a method
purely on the return type.
Usually, all methods are called in scalar context. If the method has been
declared to return an array, the Perl method is expected to return either a
reference to a Perl array containing elements of the correct type or a .NET
array.
For pure Perl interfaces, especially when used to wrap existing modules, it is
useful to be able to invoke a method in list context and automatically
transform the returned list into a .NET array of the correct type. This can be
done with the wantarray method modifier:
=for interface
static wantarray str[] SomeWords();
=cut
sub SomeWords {
return qw(a list of some words);
}
The .NET Framework uses typed properties. These properties are accessed via
getter and setter methods.
Properties are accessed using the Perl hash reference syntax:
$obj->{Property} = $value;
print "Property is $obj->{Property}\n";
It is illegal to have a property and a method of the same name. Therefore, it
is also possible to retrieve a property value using method call syntax:
printf "Property is %s\n", $obj->Property;
However, setting the value of the property must always use the hash reference
syntax.
Static properties can be accessed using the PerlNET::get() and
PerlNET::set() helper functions:
my $prop = PerlNET::get("MyType.Property");
PerlNET::set("MyType.Property", $value);
A property is defined by an access modifier, followed by the type, followed by
the property name. The getter and setter methods both invoke the Perl method of
the same name as the property. The setter method supplies the new value as the
argument:
=for interface
protected str MyProp;
=cut
sub MyProp {
my($this, $value) = @_;
unless (defined $value) {
# return current value
}
# set property to $value
}
Properties can potentially take parameters. They are not supported with special
syntax in PerlNET. They can be accessed through the method call interface: the
setter method is called set_PropertyName() and the getter is
get_PropertyName().
This release of PerlNET does not support the definition of properties with
parameters.
Many types have the concept of a "default indexer". This is just a property,
taking (usually) a single argument. Indexers are used for syntactic sugar to
directly access elements of collections without having to explicitly use a
property name.
PerlNET uses array reference syntax to implement indexers:
print "The second character is $str->[1]\n";
Due to the way Perl works internally, this only works if the indexer expects an
int parameter. In all other cases, the indexer has to be invoked using
method syntax. Usually, the name of the indexer property is Item, but some
types use a different name (e.g. System.String and
System.Text.StringBuilder use Chars).
The example above rewritten with method syntax would be:
printf "The second character is %s\n", $str->get_Chars(1);
This release of PerlNET does not support the definition of indexed properties.
Fields are similar to properties. The difference is that they are not accessed
via accessor methods but directly through the memory location.
Fields are accessed using Perl hash reference syntax. It is illegal to have a
property and a field of the same name. Fields never take a parameter.
Static fields are accessed using PerlNET::get() and PerlNET::set(), just
like static properties. Additionally, the object's own fields can be accessed
through Perl package variables of the same name. PerlNet automatically ties
these global variables to the corresponding .NET field:
package MyClass;
=for interface
static field str Name;
=cut
$Name = "Foo";
# implicitly does the same as
PerlNET::set("MyClass.Name", "Foo");
Fields are defined as properties. In addition, you need to specify the field
modifier:
private field int Count;
PerlNET stores .NET value types as boxed objects, maintaining their type
information, instead of converting them to Perl integers and numbers. This
makes it easier to pass these values to .NET methods.
PerlNET also provides built-in constructors for the standard value types:
use PerlNET qw(char);
# ...
my $ch = char('a');
The PerlNET::char() constructor/cast operator produces a System.Char
value. The following table shows the mapping between PerlNET names and the
System.* typenames.
PerlNET System Example
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 UInt64 ulong("-1")
float Single float(3.14)
double Double double(1.0)
decimal Decimal decimal("7")
PerlNET automatically converts all value types into strings as needed and also
implements autoincrement and autodecrement operators for them, covering the
whole valid value range. The 'wrap-around' behavior is the same as of the
underlying .NET types:
my $byte = PerlNET::byte(255);
++$byte;
...yields the same result as:
my $byte = PerlNET::byte(0);
However, normal arithmetic operations convert the .NET value types into Perl
integers or floating point numbers. Consequently, the result is no longer a
.NET value type and that values exceeding 32-bit integers (for long, ulong and
decimal types) may not be converted correctly. Numeric comparisons for these
three types will not work either. (Overloaded arithmetic operations are under
consideration for a future release of 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) );
Note that you will override the built-in int() function if you import
PerlNET::int() into your program:
use PerlNET qw(int);
# ...
# use CORE::int() to call builtin Perl int() function
my $int = CORE::int($val*1.68);
PerlNET treats System.Boolean values the same as Perl boolean expressions:
in numeric context they evaluate to 1 or 0 and in string context to "1" or ""
(the empty string).
PerlNet also defines the constants <PerlNET::true> and PerlNET::false, which
are the same as PerlNET::bool(1) and PerlNET::bool(0), respectively.
PerlNET::decimal(NUMBER) always assumes that the character '.' is used for
the decimal point, and that the character ',' can be ignored as a thousand
separator. Please use the System.Convert class to construct <System.Decimal>
values from localized strings:
# Locale with ',' decimal point and '.' thousands separator
my $decimal = PerlNET::call("System.Convert.ToDecimal", "123.456,78");
...or:
use PerlNET qw(AUTOCALL);
# ...
my $decimal = System::Convert->ToDecimal("123.456,78");
Enumerations provide symbolic names to numeric (integer) constants.
Enumeration members can be accessed using the PerlNET::enum() helper
function:
my $monday = PerlNET::enum("System.DayOfWeek.Monday");
All enumeration objects return their numeric values when used as numbers and
return a text representation when used as strings. The following expressions
are both true:
$monday eq "Monday"
$monday == 1
This release of PerlNET does not support the definition of enumeration types.
Delegates are constructed like any other .NET objects. The first argument to
the constructor is an object pointer and the second one is the method name of
the callback function:
my $handler = System::EventHandler->new($this, "callback");
This release of PerlNET does not support delegates on pure Perl types.
head2 Defining Delegates
This release of PerlNET does not support the definition of delegates.
When a Perl method dies, the exception message is propagated to the .NET
environment and can be caught there. The Perl exception message from $@ is
wrapped into an PerlRuntime.PerlException object. It is also possible to
throw a .NET exception object directly:
die System::ApplicationException->new("something is wrong");
Exceptions thrown in .NET code can be caught in an eval block from within Perl.
Custom attributes are applied to elements of an interface specification. The
attribute constructor is called from within square brackets placed directly in
front of the element:
=for interface
[ObsoleteAttribute("Method() will be removed in the next version")]
void Method();
=cut
If the attribute name ends with "Attribute", this suffix can be omitted.
The syntax for custom attributes in PerlNET is exactly the same as for C#. An
optional target modifier can be used to apply attributes to other elements
other than the one that immediately follows. For example, the "assembly" or
"type" targets apply the attribute to the assembly or the class itself, not to
the next method, field or property:
=for interface
[assembly: System.Reflection.AssemblyVersion("1.2.3.4")]
[type: Obsolete("You should use MyOtherType instead")]
=cut
In addition to the targets defined by the C# language PerlNET also implements
the "set" and "get" targets, which can only be specified on front of a property
definition. They will apply to the property accessors and not to the property
itself.
PerlNET also uses the custom attribute syntax to specify the base class,
implemented interfaces and attributes of the PerlNET interface. Those use the
pseudo targets 'extends', 'implements' and 'interface' respectively. But they
are not .NET level custom attributes.
Attribute parameters are limited to the following types:
-
bool, byte, char, double, float, int, long, short, string
-
the type object
-
the type System.Type
-
an enum type
-
a single-dimensional array of the above types
The values for attribute parameters must be compiletime constants and have to
be specified in C# syntax. The following sample assumes that you have defined
attributes like BooleanAttribute, taking a bool parameter, etc.
=for interface
[Boolean(true)]
[Byte(255)]
[Char('\x263a')]
[Double(3.14)]
[Float(2.3f)]
[Int16(255)]
[Int32(0x7fffffff)]
[Int64(-9223372036854775808L)]
[String("Perl")]
[Enum(DayOfWeek.Monday)]
[Type(typeof(System.String))]
[Object("string")]
[Array(new object[]{"string", 3.14, typeof(System.String)})]
=cut
Custom attributes are .NET classes that inherit from the System.Attribute
class. They must be marked with the System.AttributeUsageAttribute and
should have a name that ends with 'Attribute'. The .NET framework documentation
contains more information about custom attributes. Here is a sample attribute
defined in Perl:
package MyAttribute;
use namespace "System";
=for interface
[extends: Attribute]
[type: AttributeUsage(AttributeTargets.All, AllowMultiple=true)]
static MyAttribute(string label);
readonly string Label;
private field string label;
=cut
sub MyAttribute {
my($this,$label) = @_;
$this->{label} = $label;
}
sub Label {
my($this) = @_;
return $this->{label};
}
It can be used in a PerlNET component like this:
=for interface
[assembly: My("assembly")]
[type: My("type")]
[My("constructor"), My("can be used multiple times")]
void Sample();
[method: My("method")]
[return: My("return")]
void Method([My("param1")]string Param1, [param: My("param2")]int Param2);
[My("property")]
[get: My("getter")]
[set: My("setter")]
string Property;
[My("field")]
field string Field;
=cut
If you provide a static method called Main() in your component and compile
the component into an .exe file, PerlNET makes this function the main entry
point. This is primarily useful if you need to specify custom attributes for
the Main() function (e.g. [STAThread] for Windows Forms).
Single-dimensional arrays can be accessed with the normal array indexer syntax:
my $sum = $a->[0] + $a->[1];
print join(":", @$a), "\n";
$a->[0] = int($a->[0] + 2);
You can create so called 'jagged' arrays in .NET, which basically are arrays of
arrays. They work similarly to arrays of arrays in Perl, except that all
elements need to have the same type in .NET. You can access them using chained
indexer syntax:
print $a->[1]->[2], "\n";
print $$a[1][2], "\n";
True multi-dimensional arrays must be accessed using the GetValue() and
SetValue() methods of their System.Array base class:
my $sum = $a->GetValue(0,0,0) + $a->GetValue(1,1,1);
# use "Int32[]" arguments to GetValue() if the array rank is greater than 3
my $sum = $a->GetValue("Int32[]"->new(0,0,0,0)) +
$a->GetValue("Int32[]"->new(1,1,1,1));
Array objects can be constructed using [] syntax in the constructor:
my $a = "System.Int32[10]"->new();
my $b = "System.Int32[]"->new(2..5);
my $c = PerlNET::ctor("System.String[2,2,]", "aa" .. "bb");
PerlNET methods can return single-dimensional arrays of any .NET type,
including array of arrays. If the Perl code returns a Perl array reference,
PerlNET converts this automatically into a .NET array of the specified type.
=for interface
DayOfWeek[] Enum_Array();
int[][] Array_of_Array();
=cut
sub Enum_Array {
return [undef, "Monday", PerlNET::enum("DayOfWeek.Tuesday"), 3, "4"];
}
sub Array_of_Array {
return [[3,4],[5,6,7],[]];
}
Multi-dimensional return values must be specified as either System.Object or
System.Array, and then need to be casted to the right type on the caller
side.
|