Extension Methods (Writing)

From The Oxygene Language Wiki

Jump to:navigation, search

This is a Language topic
Feel free to add your notes to this topic below.



How to create your own Extension Methods

Introduction to Extension Methods

Extension methods were first introduced to support LINQ.

Extension methods are a way to define class methods of a new class, so that they appear to be the member of another class. Sounds crazy, but is very easy to understand:

Let's say you want to create an "implode" method for arrays. You can create a static class with a class method Implode that takes a string array and a glue string and returns a string consisting of all the parts.

var parts := array of String(['foo', 'bar', '42']);    
  Console.WriteLine(ArrayExtender.Implode(parts, ','));

This has some disadvantages:


With an extension method you can extend the members of the string array by the implode method:

var parts := array of String(['foo', 'bar', '42']);    
  Console.WriteLine(parts.Implode(','));

Extension methods were introduced into C# in version 3.0 and though it looks as if they require the .NET framework 3.5 that's not true: Extension methods are replaced by the "normal" static methods at compile time, so there are no requirements regarding the runtime version. Delphi Prism can use extension methods with .NET 2.0, too.

Defining Extension Methods

Defining an extension method is done by defining the method outside of any class and prefixing it with extension:

extension method (array of String).Implode(glue : String) : String;

(* ... *)

extension method (array of String).Implode(glue : String) : String;
begin
  var sb := new StringBuilder;  
  for p in self index i do
  begin
    sb.Append(p);
    if i < Length-1 then
      sb.Append(glue);
  end;
  result := sb.ToString;
end;

Now you can call the Implode method like any other member of the string array, if the namespace of the extending class is used. It's convenient to define the extending class in the same namespace the extended class is defined in.

By default, an extension method is public. If you want to specify a visibility for an extension method, place the visibility specifier after the declaration:

extension method (array of String).Implode(glue : String) : String; assembly;

Old Extension Methods Syntax

The old syntax is necessary if you want to place the extension in your own defined class, for example for documentation purpose or when the assembly must be used by other.

Defining the extension method is done by applying an extension attribute to a static class and its class method:

type
  [&Extension]
  StringArrayExtension = public static class
  public
    [&Extension]
    method Implode(&Array: array of String; glue : String) : String;
  end;

(* ... *)

method StringArrayExtension.Implode(&Array: array of String; glue : String) : String;
begin
  var sb := new StringBuilder;  
  for p in &Array Index i do
  begin
    sb.Append(p);
    if i < &Array.Length-1 then
      sb.Append(glue);
  end;
  result := sb.ToString;
end;

Note that there is no class before the definition of Implode because all members in a static class are static.

The ExtensionAttribute

The ExtensionAttribute must be defined in the System.Runtime.CompilerServices namespace and may not be defined in the same assembly the extending class is defined in. You have several ways to get the ExtensionAttribute class:

The first two possibilities have the advantage that you get the pre-defined methods of the System.Linq namespace, which are extremely useful.

With the new extension method syntax there's no need to apply this attribute manually anymore, but the attribute itself is still required to exist.


Interfaces and Extension Methods

Extension methods can be very powerful when extending interfaces instead of classes. Look at this changed version of the code above:

namespace System.Collections.Generic;

interface

extension method IEnumerable<T>.Implode(glue : String) : String;

(* ... *)

extension method IEnumerable<T>.Implode(glue : String) : String;
begin
  var sb := new System.Text.StringBuilder;  
  for p in self do
  begin
    sb.Append(p);
    sb.Append(glue);
  end;
  sb.Remove(sb.Length-glue.Length, glue.Length);
  result := sb.ToString;
end;

This extension method works on the IEnumerable<T> interface and, of course, every class implementing it. The extending class is placed in the System.Collections.Generic namespace, so you don't have to put something additional in your uses clause to have this extension method.

var parts := array of Integer([1,2,3,4,5]);
Console.WriteLine(parts.Implode(','));


See also


Oxygene-48.png

Area: Oxygene Language
Compiler version: Oxygene 5

Language GlossaryKeywordsTypesFAQHow To

Navigation
Areas
More
Toolbox