The C# (and presumably VB) compiler just looks for an attribute with the right namespace and name (System.Runtime.CompilerServices.ExtensionAttribute). If you're using .NET 2.0 you can get extension methods just by declaring you own attribute with that name and using it.
This is also another area where C# took a narrow view of language features and implemented the bare minimum needed to get LINQ working. Why not take a general view of type extensions, and support type extensions as a proper feature versus a hack for LINQ? That is, support all members, including static members, for type extensions.
I also prefer F#'s more general approach, which allows all kinds of extension members (static or instance, methods, properties, and events). But members of the C# team have repeatedly explained that they were the "long pole" during the LINQ work, so I think it's perfectly understandable that they took a more limited approach.
That explanation works for C# 3, but there have been two major releases since, which added relatively little to the language. They could have rounded out the LINQ-only stuff by now.
One of my favorite uses of extension methods is to have semi-intelligent type coercers/converters.
For example:
public static string AsString(this object me, string defaultValue = null)
public static int AsInt(this object me, int defaultValue = default(int))
public static float AsFloat(this object me, float defaultValue = default(float))
public static double AsDouble(this object me, double defaultValue = default(double))
public static Uri AsUri(this object me, Uri defaultValue = null)
public static bool AsBool(this object me, bool defaultValue = default(bool))
It's handy because you can do null checking in the extension method. Additionally, you can add some extra logic for converting specific runtime types. For example, calling .AsBool() on a string can check if the contents of the string is "1" or "true".
public static int ToInt(this BaudRate baudRate)
{
switch (baudRate)
{
case BaudRate.BaudUnsupported:
return 0;
case BaudRate.Baud1200:
return 1200;
case BaudRate.Baud2400:
return 2400;
case BaudRate.Baud4800:
return 4800;
case BaudRate.Baud9600:
return 9600;
case BaudRate.Baud19200:
return 19200;
case BaudRate.Baud38400:
return 38400;
case BaudRate.Baud57600:
return 57600;
case BaudRate.Baud115200:
return 115200;
case BaudRate.Baud230400:
return 230400;
default:
throw new ArgumentOutOfRangeException("baudRate");
}
Not exactly the best example (and not necessarily the best way to do that), but it's a quick one.
This is also another area where C# took a narrow view of language features and implemented the bare minimum needed to get LINQ working. Why not take a general view of type extensions, and support type extensions as a proper feature versus a hack for LINQ? That is, support all members, including static members, for type extensions.