W C# 3.0 pojawiają się nowe konstrukcje językowe, między innymi tzw. metody rozszerzeń (Extension Methods). Bez większego rozwodzenia się, poniżej krótki przykład.
using System;
namespace TestExtMethods
{
class Program
{
static void Main(string[] args)
{
"kot".TestExt();
}
}
public static class Extensions
{
public static string TestExt(this string ala)
{
return string.Format("testExt: {0}", ala);
}
}
}
Wygląda na to, że metoda TestExt rozszerza nam wbudowaną klasę System.String. Nie jest to metoda ‘zaprzyjaźniona’ z dostępem do składowych prywatnych klasy string, właściwie poza słowem kluczowym ‘this’ pojawiającym się na liście argumentów metody niczym nie różni się od zwykłej metody statycznej. Czyżby?
Gdy zajrzymy do kodu IL wygenerowanego przez kompilator, wszystko staje się jasne. W rzeczywistości metody rozszerzeń są zwykłymi metodami statycznymi z dodatkowym atrybutem ‘znacznikiem’ dla intellisense i kompilatora (Reflector również potrafi ten atrybut właściwie zinterpretować):
[System.Runtime.CompilerServices.Extension]
public static string TestExt(string ala)
{
return string.Format("testExt: {0}", ala);
}
W rzeczywistości, przy próbie dodania takiego atrybutu do metody statycznej otrzymujemy błąd kompilacji CS1112: "Do not use 'System.Runtime.CompilerServices.ExtensionAttribute'. Use the 'this' keyword instead."
Czy ta wiedza coś nam daje?
Skoro metody rozszerzeń to zwykłe metody statyczne statycznych klas, to możemy wywoływać je jak każde inne. Tym samym, jeśli ktoś dostarczył nam bibliotekę zawierającą metody rozszerzeń o takich samych nazwach jak te, które my zdefiniowaliśmy w swoich modułach, to żeby uniknąć błędu kompilacji ‘The call is ambiguous...’, który pojawi się, jeśli będziemy mieli w kontekście dwie klasy statyczne definiujące metody rozszerzeń o tej samej nazwie, możemy wywoływać bezpośrednio odpowiednie metody rozszerzeń, informując tym samym kompilator, o którą metodę nam chodzi:
static void Main(string[] args)
{
Extensions.TestExt("kot");
}
i unikając tym samym błędu kompilacji. Co ciekawe, Reflector znowuż zinterpretuje powyższe wywołanie jako użycie metody rozszerzeń i w podglądzie C# znowuż dostajemy ""kot".TestExt();"