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:
PerlNET features include:
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 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 2 additional libraries at runtime, Perl512RT901.dll and Perl512NH901.dll, for ActivePerl 5.12. For older ActivePerl versions "512" is replaced by "510" for ActivePerl 5.10, or "58" for ActivePerl 5.8. An additional suffix "_64" is used by the 64-bit version of PerlNET, Perl512RT901_64.dll and Perl512NH901_64.dll. You will need to distribute copies of these 2 DLLs with your .NET assemblies.
Perl512NH901.dll can be found in the PDK 9.0.1 install location.
C:\Program Files\ActiveState Perl Dev Kit 9.0.1\bin
Perl512RT901.dll is installed in the Global Assembly Cache (GAC) in this directory:
C:\WINDOWS\assembly\GAC_MSIL\Perl512RT901\9.0.1.31238__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:
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.