Generic extension methods
I was playing around with the compiler when I hit this interesting feature. I was very surprised to see that this has compiled successfully.
1: static class Program2: {
3: static void Main(string[] args)4: {
5: IProcesser<GZipStream> p = null;6: p.HasTimeout();
7: }
8: }
9:
10: public static class Extensions11: {
12: public static bool HasTimeout<T>(this IProcesser<T> s)13: where T : Stream14: {
15: return s.Desitnation.CanTimeout;16: }
17: }
18:
19: public interface IProcesser<TDestination>20: where TDestination : Stream21: {
22: TDestination Desitnation { get; }
23: }
Comments
Maybe I'm missing something but any reason it shouldn't?
Because it's a nulled variable?
You can do the same with any method on a variable:
Sure it'd fail during execution, but not on compile.
We figured this out a while back, and do a test for a null reference that throws an exception, if it's a critical part of the app.
Andrew,
The extension method is a generic method.
I was surprised that that worked.
Actually, I did look at the top afterwards, at the name of the post - and yeah... red faced now :)
Yep, we have a couple of generic methods as extension methods too - comes nice and handy when you need to do the same thing in a few different cases.
We're starting to build up some core extension methods to be shared between specific projects, and each project has it's own extension method library - but we do like having some generics used on the global ones that can work between a couple of project idiosyncrasies.
Apologies to the assume, it did make an ass out of me, at least :)
There are two things about this that puzzle me - firstly and superficially, there is a spelling mistake - Desitnation instead of Destination - but it doesn't matter to the compiler.
But, there's no implementation of the getter (TDestination Desitnation {get;}) That is weird!
Oh, and if you're going to show us code, it would be nice if it didn't have line numbers - it makes it terrible to cut and paste.
I am trying to find my previous code highlighter.
As for why it compiles, that is an interface, not a class
It seems like one must check for a not null "this" argument as a precondition in extension methods.
Also, the compiler is smarter with generic methods. Notice you don't have to specify the parameters to any generic System.Linq ext methods.
Two typical linq extension methods include
public static IQueryable AsQueryable(this IEnumerable source);
public static IQueryable<TElement> AsQueryable<TElement>(this IEnumerable<TElement> source);
If u have the code
string[] items = {"a","b"."c" ,"two"};
items.ToQueryable();
the second line will use the generic method and not the first signature.
Is this a case of the compiler inferring type when picking call methods?
Why are you surprised?
Isn't the whole LINQ stuff based on extension methods for the (generic) IEnumerable<T> interface?
Well, maybe I am missing the obvious, but I don't see anything strange. It's a static method behind the scenes, so the compiler shouldn't care whether you are passing a null argument to it. I don't see why being a generic method should change that fact.
VIjay ,
Yes, it does.
It is significantly smarter than it was for 2.0
Actually, the thing I find most surprising is that the type inference works. I'm pretty sure the pretty similar code you could write for .NET 2.0 doesn't compile.
The whole "extension methods aren't instance methods" is a bit weird, although I have to admit I've already abused it. (i.e. made it not throw an exception)
Thomas,
Methods for generic interface, yes.
Generic methods, I wouldn't have expected that.
Albert,
I am surrised that the compiler can pick it up, actually.
Awww old news
http://grabbagoft.blogspot.com/2007/07/constrained-generic-extension-methods.html
It's very interesting indeed. I've been playing around with specification-type extensions, for things like IComparable<T>.
It was very surprising the first time I compiled it. We had a little bet at the office whether it would or not. And an interesting DBC-style implementation too:
http://eltonomicon.blogspot.com/2007/10/static-class-extensibility-via.html
You do still run into the problems of generic constraints, and you have to weigh whether you target the constraint directly or use constraints.
Ayende,
Even talking about generic methods, LINQ is still stuffed full of examples.
Take Select - yes, it acts on a particular generic interface, but it has to project to another generic interface (a sequence of the result type) and that result type is specified by a type parameter.
Indeed, even simple methods like Where which only act on a generic interface are still generic methods - it's not like the type itself is Enumerable<T> with extension methods for the corresponding IEnumerable<T>. There are relatively few methods in LINQ which aren't generic methods.
Jon
Also, not only does it compile successfully, but your generic constraints are correctly interpreted by Intellisense. It's not all IProcessor<T> implementations that catch it, it's only those that conform to the constraint.
Even removing the constraint on the interface, the Intellisense still pops up correctly if you constrain the generic method. I haven't tried R# code completion yet, this is only VS built-in intellisense.
Ayende, I've been using the following code (which will be mangled by the blog comment engine, grrrr) for a while:
public static string Join(this IEnumerable<string> strings, string delimiter)
{
}
I also echo Thomas Krause: LINQ is built on IEnumerable<T> and extension methods working with it, among other things.
Ayende,
Maybe this is by design (callvirt vs. call). This blog post explains it: http://blogs.msdn.com/howard_dierking/archive/2007/02/09/more-c-3-0-extension-methods.aspx
Here's another one:
http://bradwilson.typepad.com/blog/2008/01/c-30-extension.html
I'm really surprised that you had not come across this already. I've been doing this since early CTP releases. Generic extension methods are fundamental to C# 3.0 programming.
Glad you found it. Now I'm sure you'll come up with lots of interesting uses for this feature.
Comment preview