Advanced  Services Extension Methods Back Next Up Home


Extension Methods

Erweiterungsmethoden sind ein klassisches Beispiel für das, was man "syntactic sugar" nennt. Erweiterungsmethoden ermöglichen es bestehende Klassen nachträglich um Instanzmethoden zu erweitern ohne den Quellcode der zugehörigen Klassen zu verändern. Da dies aber grundsätzlich nicht möglich ist, kann es nur eine Sache der Syntax sein. Es sieht auf den ersten Blick so aus, als ob Erweiterungsmethoden auch vererbt werden können, den Instanzen abgeleiteter Klassen können diese verwenden. Bei näherer Betrachtung allerdings stellt sich heraus, daß das nicht stimmt, denn Erweiterungsmethoden können nicht überschrieben werden, denn tatsächlich sind Erweiterungsmethoden statische Methoden in einer eigenen Klasse. Durch eine spezielle Syntax ist es jedoch möglich, einen Bezug zu einer anderen Klasse herzustellen und dadurch erscheinen sie als Instanzmethoden dieser Klasse. So gibt es auch zwei Arten Erweiterungsmethoden aufzurufen, einmal als statischen Methoden in der sie definierenden Klasse und zum anderen als Instanzmethoden zu Instanzen der Klasse, auf die man in der implementierenden Klasse Bezug nimmt.


Extension Methods für reale Klassen

Ein Beispiel wird diese Sätze klar machen. Die folgende Klasse enthält zwei statische Methoden, die als Instanzmethoden für die Klasse String erscheinen.

/*
*/
public static class ExtensionClassForString
{
   public static string ReverseChars(this string st)
   {
      if (st.Length > 0)
      {
         char[] array = st.ToCharArray();
         Array.Reverse(array);
         return new string(array);
      }
      return st;
   }

   public static bool IsPalindrom(this string st)
   {

      if (st.Equals("") || st.Length == 1) return true;

      string rev = st.ReverseChars();
      if (st.Equals(rev)) return true;

      return false;
   }
}  // end Extension class

Es fällt auf, daß dem Typ des Parameters das Schlüsselwort this vorangestellt wird. Diese Methoden kann man nun einfach als statische Methoden verwenden und das this ignorieren:

ExtensionClassForString.ReverseChars(st);
bool boo = ExtensionClassForString.IsPalindrom(st);
Console.WriteLine(st);
Console.WriteLine("IsPalindrom: " + boo);

Andrerseits signalisiert das this dem Compiler, daß auch der folgende Aufruf erlaubt ist.

st = st.ReverseChars(); // called like an instance method.
bool boo = st.IsPalindrom();
Console.WriteLine(st);
Console.WriteLine("IsPalindrom: " + boo);

Die Erweiterungsmethode erscheint auch in der Intellisense:

extensionmethod-in-intellisense-1.jpg

Das folgende Beispiel zeigt, daß eine Erweiterungsmethode auch mehrere Parameter haben kann und wie sie gerufen wird. Dabei erhält nur der erste Parameter das Schlüsselwort this.

namespace ExtensionMethods
{
   class Test
   {
      private int i = 17;
      protected int j = 42;
      internal int k = 71;
   }

   static class ExtensionForTest
   {
      public static void SomeMethod(this Test t)
      {
         //t.i = 71;
         // i is inaccessible due to its protection level
         //t.j = 71;
         // j is inaccessible due to its protection level
         t.k++;  // internal sichtbar, da im gleichen assembly
         Console.WriteLine("SomeMethod");
      }

      public static void SomeOtherMethod(this Test t, int i)
      {
         Console.WriteLine("SomeOtherMethod");
      }
   }


   class ChildOfTest : Test
   {
      public ChildOfTest()
      {
      }
   }
}

Der Aufruf

/*
*/
private static void Example_ExtensionForTest()
{
   Test te = new Test();
   te.SomeMethod();
   te.SomeOtherMethod(17);
}

Weiter sieht man, daß auch für die Kindklasse die Erweiterungsmethode in der Intellisense erscheint.

extensionmethod-in-intellisense-2.jpg

Der Versuch, die Extension Medthod in der Ableitung zu überschreiben muß jedoch scheitert.

extensionmethod-in-intellisense-3.jpg


Extension Methods für abstrakte Klassen

Auch für abstrakte Klassen ist es möglich Erweiterungsmethoden zu schreiben. Wie im vorigen Fall müssen sie allerdings reale Methoden sein, da statische Methoden nicht abstrakt sein können.

abstract class AbstractClass
{
   public abstract void AbstractMethod();
   public void RealMethod()
   {
   }
}


static class AbstractClassExtension
{
   // a static member cannot be marked as override, virtual, or abstract
   // must declare a body because it is not marked abstract, extern, or partial
   public static void ExtensionMethod(this AbstractClass a)
   {
   }
}

Extension Methods für Interfaces

Dasselbe gilt für Interfaces und hier ist es ungewohnt, daß einem Interface eine reale Methode zugeordnet ist, aber sie ist ja nicht tatsächlich Teil des Interfaces

interface IExtensionFace
{
   void Method();
}

static class InterfaceExtension
{
   // must declare a body because it is not marked abstract, extern, or partial
   // a static member cannot be marked as override, virtual, or abstract
   // also muß sie implementiert werden...
   public static void ExtensionMethod(this IExtensionFace ef)
   {
      Console.WriteLine("ExtensionMethod");
   }
}

Hier eine Beispielimplementierung des Interfaces, das die Erweiterungsmethode im Konstruktor verwendet.

/*
   Das Interface markieren -> Kontextmenü -> Implement Interface
*/
class IExtensionFaceImpl : IExtensionFace
{
   public IExtensionFaceImpl()
   {
      // kann verwendet werden
      this.ExtensionMethod();
   }

   public void Method()
   {
      Console.WriteLine("method");
   }
}

Natürlich kann auch in diesem Fall die Erweiterungsmethode nicht überschrieben werden.


Extension Methods in einem separaten Assembly (DLL)

Mit einem separaten Eassembly ist es möglich die Erweiterungsmethoden zu exportieren. Bindet man das Assaembly über using in ein Projekt ein, kann man die Extensionmethoden verwenden und sie erscheinen als Quasi-Instanzmethoden in der Intellisense.

Im folgenden legen wir ein Assemblyprojekt an und erstellen aus dem Code eine DLL (siehe dazu assemblies.html)

namespace ExtensionLibrary
{
   /*
   */
   public static class ExtensionForString
   {
      public static string ReverseChars(this string value)
      {
         if (value.Length > 0)
         {
            char[] array = value.ToCharArray();
            Array.Reverse(array);
            return new string(array);
         }
         return value;
      }

      public static bool IsPalindrom(this string value)
      {

         if (value.Equals("") || value.Length == 1 ) return true;

         string rev = value.ReverseChars();
         if (value.Equals(rev)) return true;

         return false;
      }
   }
}  // end namespace ExtensionLibrary

Und hier ein Projekt, das die Erweiterungsmethoden verwendet.

using System;
////////////////////////////////
using ExtensionLibrary;
////////////////////////////////

namespace ExtensionMethodsFromLibrary
{
   class ExtensionMethodsFromLibrary
   {
      static void Main(string[] args)
      {
         string st = "abc";
         string rev = st.ReverseChars();
         Console.WriteLine(rev);

      }
   }
}







Valid XHTML 1.0 Strict top Back Next Up Home