Extension Methods v C# 3.0 - k čemu jsou dobré a jak je psát

V nejnovější verzi 3.0 jazyka C# (z důvodu zmatení nepřítele je součástí .NET Frameworku 3.5) je dostupná nová technologie Extension Methods. Extension methods vám umožňují přidat třídě instanční metodu "zvenčí", aniž byste museli modifikovat třídu samotnou.

Jak to funguje

Vytváření extension methods je jednoduché. Stačí vytvořit statickou třídu a v ní vytvořit statickou metodu. První argument této metody jest třeba uvodit klíčovým slovem this.

Jakmile toto uděláte, tak ve všech třídách, které předmětnou statickou třídu vidí (importují její namespace) můžete tuto metodu volat tak, jako by byla součástí třídy, která je jejím prvím parametrem.

Praktický příklad

Pokud vám předchozí dva odstavce nedávají smysl, nebojte se. Je to ve skutečnosti mnohem méně složité, než jak to vypadá.

Poměrně často potřebuji převést jeden konkrétní sloupeček z DataTable do jednorozměrného pole stringů. Klasické řešení podobných případů spočívá v tom, že si vytvořím nějakou třídu jménem Utils nebo tak nějak, do které tuto funkcionalitu schovám - vytvořím jednorozměrné stringové pole a zkopíruju do něj obsah nultého sloupečku tabulky. Třídu pak používám tak, že napíšu něco jako string[] pole = Utils.TableToArray(table);.

Extension methods nabízejí elegantnější řešení. Jeho zdrojový kód vypadá následovně:

using System.Data;

 

namespace Altairis.IdentityToolkit.UserMap {

    internal static class ExtensionMethods {

 

        public static string[] ToStringArray(this DataTable table, int columnIndex) {

            string[] data = new string[table.Rows.Count];

            for (int i = 0; i < table.Rows.Count; i++) data[i] = System.Convert.ToString(table.Rows[i][0]);

            return data;

        }

 

    }

}

Kouzelné slovíčko this před názvem první tabulky způsobí, že v jakékoliv třídě, která bude ve stejném namespace jako třída ExtensionMethods, nebo ho bude importovat, budu moci napsat něco jako:

DataTable dt = new DataTable();

// Tady dt nějak naplním, třeba pomocí SqlDataAdapteru

string[] pole = dt.ToStringArray(0);

Vše je samozřejmě strongly-typed, k dispozici je IntelliSense a podobné vymoženosti.

Extension methods mohou být overloadované, případně mohou overloadovat již existující metody. Moje metoda ToStringArray tak má celkem tři overloady: pokud ji zavolám bez parametrů, převede na pole nultý sloupeček tabulky. Tento případ je nejčastější, dotazy tohoto typu většinou ostatně mívají jenom jeden sloupeček jako výsledek. Případně ji mohu zavolat s celočíselným parametrem, který značí index sloupce, který se má použít. Nebo ji mohu zavolat se stringovým parametrem, který značí název sloupce:

using System.Data;

 

namespace Altairis.IdentityToolkit.UserMap {

    internal static class ExtensionMethods {

 

        public static string[] ToStringArray(this DataTable table) {

            return ToStringArray(table, 0);

        }

 

        public static string[] ToStringArray(this DataTable table, string columnName) {

            return ToStringArray(table, table.Columns[columnName].Ordinal);

        }

 

        public static string[] ToStringArray(this DataTable table, int columnIndex) {

            string[] data = new string[table.Rows.Count];

            for (int i = 0; i < table.Rows.Count; i++) data[i] = System.Convert.ToString(table.Rows[i][0]);

            return data;

        }

 

    }

 

}

Filozofická odbočka

Extension methods v .NET Frameworku přibyly kvůli LINQu. Jeho elegantní konstrukce by bez nich nebyly možné. Můžete je ovšem využívat i bez něj. Samozřejmě, že se bez nich dokážete obejít. Nepřinášejí nic úplně převratného, jenom drobné ulehčení zápisu - syntaktický cukr.

Se syntaktickým cukrem je to podobně, jako se skutečným kořením v reálném životě. Je nutné, aby ho bylo přiměřené množství. A přiměřené je subjektivní parametr, což jsem zjistil po půlročním soužití s člověkem, který do libovolného jídla bez ohledu na jeho povahu a původ nasype několik lžic soli, půl lahve kečupu a větší množství sojové omáčky.

Pokud je syntaktického cukru málo, dokážete v daném programovacím jazyce (je-li Turing complete) napsat cokoliv, ale nebude to moc přehledné. Programovací jazyk může mít pouhých osm instrukcí - vzor elegance a stylové čistoty - leč pak bude populární aplikace Hello World vypadat třeba takto:

++++++++++[>+++++++>++++++++++>+++>+<

<<<-]>++.>+.+++++++..+++.>++.<<++++++

+++++++++.>.+++.------.--------.>+.>.

Na druhou stranu, pokud je syntaktického cukru příliš, podporuje to psaní příliš obskurních konstrukcí a vede ke vzniku write-only programovacích jazyků, kdy se v kódu vyzná jenom jeho autor, a to ještě jenom nepříliš dlouhou dobu poté, co ho napsal.

Podle mého subjektivního názoru vděčí .NET za úspěch tomu, že v jeho kmenových jazycích je syntaktického cukru tak akorát.

Zpět k extension methods

Pár nesourodých postřehů, které se týkají extension methods a odpovídají na nabízející se otázky:

  • Je jedno, jaká třída bude extension methods obsahovat, jak se bude jmenovat, jestli bude jedna pro všechny objekty nebo pro každou třídu jiná. Musí být jenom statická. Osobně to řeším tak, že v assembly od toho držím třídu jménem ExtensionMethods, ale to je věc osobního vkusu.
  • Extension methods nelze vytvářet jako statické, jenom instanční.
  • Neexistují žádné "extension properties" nebo "extension events". Tímto způsobem lze definovat jen a pouze metody.

Malá soutěž na závěr

Programovací jazyk o pouhých osmi instrukcích, který jsem o pár odstavců výše předvedl, je skutečný. Jeho kompilátor má 240 bajtů. První čtenář, který do komentářů napíše, jak se tento programovací jazyk jmenuje, kdo je jeho autorem a ve kterém roce vznikl, dostane za odměnu tričko.

  • Altairis
  • Nemesis
  • Microsoft MVP
  • IIS
  • ASP.NET