Extension Methods
Allow existing type to be extended with new methods without altering the definition of the original type.
An extension method is a static method of a static class, where modifier is applied to the first parameter.
// Extension methods allow an existing type to be extended with new methods without altering // the definition of the original type: // (Note that these examples will not work in older versions of LINQPad) static void Main() { Console.WriteLine ("Perth".IsCapitalized()); // Equivalent to: Console.WriteLine (StringHelper.IsCapitalized ("Perth")); // Interfaces can be extended, too: Console.WriteLine ("Seattle".First()); // S } public static class StringHelper { public static bool IsCapitalized (this string s) { if (string.IsNullOrEmpty(s)) return false; return char.IsUpper (s[0]); } public static T First<T> (this IEnumerablesequence) { foreach (T element in sequence) return element; throw new InvalidOperationException ("No elements!"); } }
Extension Method Chaining
// Extension methods, like instance methods, provide a tidy way to chain functions: static void Main() { string x = "sausage".Pluralize().Capitalize(); x.Dump(); // Equivalent to: string y = StringHelper.Capitalize (StringHelper.Pluralize ("sausage")); y.Dump(); // LINQPad's Dump method is an extension method: "sausage".Pluralize().Capitalize().Dump(); } public static class StringHelper { public static string Pluralize (this string s) => s + "s"; // Very naiive implementation! public static string Capitalize (this string s) => s.ToUpper(); }
Ambiguity and Resolution
Namespaces
An extension method cannot be accessed unless its class is in scope, by importing its namespace.
using System; namespace Utils { public static class StringHelper { public static bool IsCapitalized (this string s) { if(string.IsNullOrEmpty(s)) return false; return char.IsUpper(s[0]); } } }
To use IsCapitalized, need to import Utils namespace
namespace MyApp { using Utils; class Test { static void Main() => Console.WriteLine ("Perth".IsCapitalized()); } }
Extension methods versus instance methods
// Any compatible instance method will always take precedence over an extension method: static void Main() { new Test().Foo ("string"); // Instance method wins, as you'd expect new Test().Foo (123); // Instance method still wins } public class Test { public void Foo (object x) { "Instance".Dump(); } // This method always wins } public static class StringHelper { public static void Foo (this UserQuery.Test t, int x) { "Extension".Dump(); } }
Extension methods versus extension methods
// The extension method with more specific arguments wins. Classes & structs are // considered more specific than interfaces: static void Main() { "Perth".IsCapitalized().Dump(); } static class StringHelper { public static bool IsCapitalized (this string s) { "StringHelper.IsCapitalized".Dump(); return char.IsUpper (s[0]); } } static class EnumerableHelper { public static bool IsCapitalized (this IEnumerables) { "Enumerable.IsCapitalized".Dump(); return char.IsUpper (s.First()); } }
// The extension method with more specific arguments wins. Classes & structs are // considered more specific than interfaces: static void Main() { string[] strings = { "a", "b", null, "c"}; foreach (string s in strings.StripNulls()) Console.WriteLine (s); } static class Test { public static IEnumerableStripNulls (this IEnumerable seq) { foreach (T t in seq) if (t != null) yield return t; } }