Extension Methods in .NET - A Comment

A

Axel Dahmen

someData.Where(firstCondition)
.Where(secondCondition)
.OrderBy(firstOrdering)
.ThenBy(secondOrdering)
.Select(projection)

then I only want one item of data to be copied at a time. Your
implementation breaks that completely.

No, why should it? In fact, it does nothing else Linq does. How do you think Linq is implemented? Probably the method examples I gave weren't optimized but that doesn't make them basically different from Linq.

Again, Linq is no magic. Extension Methods makes it look magic, but it isn't.

Axel
 
A

Axel Dahmen

The fact is, it does not break OOP principles to have a static method
No one has suggested extension methods be used except in the very specific
scenario I describe above. They in fact CANNOT BE USED IN ANY OTHER
SCENARIO. And you have specifically written here that in that scenario --
THE ONLY ONE IN WHICH EXTENSION METHODS CAN BE USED -- you agree with me.
So what's your problem?

No, I didn't agree on using Extension Methods. I agreed on the fact that static methods take other objects as parameters. In fact, every function having parameters does that.

Stop flaming!

Axel
 
J

Jon Skeet [C# MVP]

Axel Dahmen said:
No, why should it? In fact, it does nothing else Linq does. How do
you think Linq is implemented? Probably the method examples I gave
weren't optimized but that doesn't make them basically different from
Linq.

They are *fundamentally* different from LINQ.
Again, Linq is no magic. Extension Methods makes it look magic, but
it isn't.

Look at what your code does.

public MyLinq<T> Select()
{
Queue<T> retVal=new Queue<T>();

foreach(T o in _coll) retVal.Enqueue(o);

return new MyLinq<T>(retVal);
}

What would happen if you passed an infinite sequence into that? LINQ
would handle it absolutely fine. Your code wouldn't.
 
A

Axel Dahmen

So you want a specialised inherited class for everything in the framework
that implements IEnumerable? What happens when I create my own IEnumerable
class and I want that to provide me with LINQ services, what do I do then?

I'm not too deep into Linq, I'm discussing about Extension Methods here. Yet, AFAIK, Linq only works on IEnumerable derived classes. As does my example... So what in fact would you do in both cases?
As a developer, I'm less fussed as to whether List<T>
truly inherits from some sort of Linq base type or if it has a series of
extension functions grafted onto it, particularly if the code is identical.

I agree with you. From the developer's point of view I don't see a difference either (except for where to take functionality from, where to find it etc.), but from the designer's point of view Extension Methods are a huge gate for proliferating bad coding.
Or where its performance has suffered due to MS adding
another layer into the inheritance hierarchy.

Just want to say that adding layers of hierarchy doesn't necessarily lack performance. Basically additional code is only executed in constructors (and destructors). But - apart from any additional code being executed - it's not more than just a function call to the base class's constructor/destructor. Anything else would have been executed as well by using any other possible concept in the world.

One of the most important rules of OOD is to use it as intended. You don't
insert a screw into wood with a hammer, so why would you demand an inherited
sub-type of List<T> when that sub type would only add some new functionality
(a half dozen new functions) rather than override base behaviour? It is
simply not necessary, it creates maintenance problems, duplicated code, and
adds performance overhead.

Actually you don't need to. As you've seen from my example I've used a MyLinq generic to do the job. There wasn't even inheritance involved here. Still, in case you wouldn't need the derived class, just use the base class. There is absolutely no reason that would keep you from creating and using base classes as long as they are not declared as being abstract.

If I seal a class, I don't want anybody to override its internal behaviour,
and that is the only condition for sealing it. I would have no problem with
anybody adding functionality to it that did *not* interfere with its
behaviour,

Please refer to my other reply where I gave an example of how to keep internal behaviour solid without sealing a class.

in the same way that I would have no problem with somebody
writing a utility class that accepted my class as a parameter and performed
work with it. In fact, I just described Extension methods right there ---
syntactical sugar.

Yep, syntactical sugar for having ignored OOA. My humble opinion still.

Perhaps so, but why is that such a problem?

Because, for instance, when implementing Extension Methods in a workgroup, debugging will become a mess. You can't boil an error down to a class's implementation. It's impossible to narrow the search. You've just have to debug the *whole* code.

I would prefer to have a set of
extension methods that are accessible via an instance of the object they
perform work on instead of some helper class.

Helper classes are usually a good example for procedural code disguised as OOD. I'd regard any such as bad. They don't help. I can't discuss with you on your example without having seen your code. But if you'd like to add a function to SqlConnection then why don't you just create a derivate class then? Or why are you using it so massively at all? It's a technical class that should be hidden somewhere in a data access layer class hierarchy.

Axel
 
A

Axel Dahmen

Using inheritance unnecessarily to add functionality is:
a) impossible in many situations (e.g. when the class is sealed)
b) undesirable in many other situations

You are right. I'd even go further: I'd say that doing *anything* unnecessarily is undesirable in many situations. ;)

Personally I prefer most classes to be sealed - there is a design cost
to inheritance, which should not be overlooked. See

http://msmvps.com/jon.skeet/archive/2006/03/04/inheritancetax.aspx

I've read your interesting article. Please refer to one of my recent posts here from today regarding how to keep a design stable without sealing.

No - inheritance is made for *specializing* behaviour, not representing
other behaviour which is still appropriate to *every* instance of the
base type.

Again I agree with you. Could you give a quick example of > representing other behaviour < that's appropriate for this discussion? I frankly can't see an issue here.

Extension methods, not expansion methods.

Yep, right. Got to hurry over the keys sometimes... Yet, where's the difference?

Two examples in my own experience:

1) String utility routines. We can't derive from string, nor could we
force every instance of string returned by the library methods to be an
instance of our own string type even if we could. Where, then, would
you write utility methods for strings? Such methods are *very* common.

Is this really common? I've never seen some kind of StringHelper class. Yet I've seen many member functions taking a string object as parameter without being string itself. Yet, all these functions didn't change a string's state. In fact they actually can't. But I know the StringBuilder class which is a perfect example of not using Extension Methods but a discrete class.

2) Stream routines, e.g. to read the whole of a stream into a byte
array, or copy the contents of one stream to another. These are very
commonly mis-implemented operations, so it's good to have them in one
place. They can operate on *any* stream, so inheritance is
inappropriate. Static method taking a stream is entirely consistent
with this situation - and extension methods make them easier to use.

Again I'd like to see a short example of how Extension Methods would to a trick common OOA implementation grounds don't cover gracefully.

Okay, so really you want to virtually abolish static methods. Sorry,
can't agree there. I rather like being able to use LINQ operations on
*any* IEnumerable<T> rather than having to construct an instance of
MyLinq<T> every time. It sounds like you want to force people to
artificially create an instance of a type for *no reason* other than to
avoid using static methods.

I don't think that's a pragmatic or sensible approach.

OK, we have a disagreement here. But still you will see that debugging Extension Methods and maintaining applications using them will become a big hassle. *That's* in fact the reason for avoiding to use static methods.
 
A

Axel Dahmen

So you've honestly never used static methods for anything? No static class
containing methods to handle, say, string-formatting specific to the data
you're dealing with? I would like to know how you've avoided creating
utility/helper classes & functions all these years, but I truly hope it's
not just a case of "I put them all into a formatting class which I
instantiate and call the methods of as needed"...

No, not at all. In my first .NET 1.0 I'd implemented an SqlHelper class indeed. In my second (.NET 1.1) then I've encapsulated SQL access into a data access layer which didn't use any static methods at all. For string formatting specific to data I'm dealing with I'm using separate objects, just as the .NET framework itself does. No, I can't remember having implemented a static method for a long time now.

I think this is indicative of you not understanding the framework. VB has
existed for much, much longer than C# and had the beginnings of OOD as of
version 4 (mid 90s?) when MS first introduced classes into the language.

Yes. I'm programming since 1977. Rest assured I know VB. But have you ever seen actual VB code? Have you seen anyone using OOD? I didn't. Just because VB offered so much procedural functionality. Just because it was providing classes doesn't mean people where using them.

When .NET was released, C# and VB.NET were released at the same time. Both
support an identical set of OOD tools. VB.NET is no more or less procedural
than C#, it's just a different syntax of what is essentially the same
language under the hood. OOD is no newer in VB.NET than it is in C#, and
arguably it's existed in VB for longer due to VB's age.

Hey hey... Did you read *anything* in my previous post that said "VB.NET"?? I just wrote that I don't know VB.NET as I didn't use it yet. Of course it has the same CLR C# and J# and any other .NET language has. Yet it has its own extensions left. Just refer to the logical operators. I just don't know them all. That's what I said. Nothing more. Nothing less.

Regards,
Axel
 
A

Axel Dahmen

Hi Patrice,

yes, I've heard the term "syntactic sugar" a lot nowadays. Which I regard as "hiding loads of code behind a tiny pretty curtain".

Regards,
Axel


----------
 
J

Jon Skeet [C# MVP]

Axel Dahmen said:
I'm not too deep into Linq, I'm discussing about Extension Methods
here. Yet, AFAIK, Linq only works on IEnumerable derived classes. As
does my example... So what in fact would you do in both cases?

LINQ to Object only works on IEnumerable<T> (and IEnumerable to some
extent) but LINQ itself is completely type-agnostic. For example, Marc
Gravell and I recently wrote a "push" version of LINQ by implementing
the same set of operators as extensions on a different interface.
I agree with you. From the developer's point of view I don't see a
difference either (except for where to take functionality from, where
to find it etc.), but from the designer's point of view Extension
Methods are a huge gate for proliferating bad coding.

Do you really not see any benefit in being able to effectively add
behaviour to interfaces without the interface implementor having to do
any work themselves?
Just want to say that adding layers of hierarchy doesn't necessarily
lack performance. Basically additional code is only executed in
constructors (and destructors). But - apart from any additional code
being executed - it's not more than just a function call to the base
class's constructor/destructor. Anything else would have been
executed as well by using any other possible concept in the world.

Well, virtual methods can't be inlined of course. I agree it's not
likely to be significant in many cases. Personally I find inheritance
has more of a *design* penalty than a *performance* penalty.
Actually you don't need to. As you've seen from my example I've used
a MyLinq generic to do the job. There wasn't even inheritance
involved here. Still, in case you wouldn't need the derived class,
just use the base class. There is absolutely no reason that would
keep you from creating and using base classes as long as they are not
declared as being abstract.

But this means we can *only* use LINQ to Objects with your class,
Please refer to my other reply where I gave an example of how to keep
internal behaviour solid without sealing a class.

Don't forget method *hiding* as well as overriding. If I were able to
declare:

FooString x = new FooString(...);

then x.Equals may well have nothing to do with the normal string.Equals
behaviour.

I'm very happy with string having been sealed. I just wish classes were
sealed by default.
Yep, syntactical sugar for having ignored OOA. My humble opinion still.

I don't think you can really claim it's your "humble" opinion when you
write things like:

"Extension Methods in .NET is a slap in the face of all serious
software engineers."

That's not humble at all.
Because, for instance, when implementing Extension Methods in a
workgroup, debugging will become a mess. You can't boil an error down
to a class's implementation. It's impossible to narrow the search.
You've just have to debug the *whole* code.

When debugging, I don't normally say "I'll look at all the code in a
certain class" - I say "I'll look at all the code in a particular flow
of execution". That will *often* include code from multiple classes;
this is no different.
Helper classes are usually a good example for procedural code
disguised as OOD.

Not in my view.
I'd regard any such as bad. They don't help. I
can't discuss with you on your example without having seen your code.
But if you'd like to add a function to SqlConnection then why don't
you just create a derivate class then?

Inheritance appears to be your answer to everything, despite its
massive issues when introducing new behaviour.

For a start, you can't always decide which type you'll actually get. If
you're using an API which returns SqlConnection, what are you going to
do? You could write a wrapper class instead, I suppose - but that's a
different answer and not significantly different to writing helper
methods.

Inheritance is great for *specializing* behaviour, but not for writing
behaviour which is valid for *all* instances of a particular class.
Extension methods are great for the latter, and work with interfaces
too.
Or why are you using it so massively at all? It's a technical class
that should be hidden somewhere in a data access layer class
hierarchy.

You make it sound as if the code in the DAL doesn't really exist. Sure,
SqlConnection shouldn't be used outside the DAL - but within the DAL,
it may be used fairly heavily.
 
A

Axel Dahmen

No.. The Linq class could be derived from, e.g., List and could be
But *then* it's restricted to Lists - what happens if I've got an array
instead? And if the Linq class were to derive from List, you'd have a
*really* broken inheritance chain.

You are not talking about Extension Methods now anymore, are you? You are talking about Implicitly Typed Local Variables in C# 3.0, not Extension Methods. Let's not argue on these please.

Regards,
Axel
 
A

Axel Dahmen

What would happen if you passed an infinite sequence into that? LINQ
would handle it absolutely fine. Your code wouldn't.

Wow, you're quick... ;)

Are we still discussing Extension Methods here? Or are we discussing LinQ? I'm still sure that everything Extension Methods did to LinQ could have been accomplished without them as gracefully.

Regards,
Axel Dahmen
 
J

Jon Skeet [C# MVP]

Axel Dahmen said:
You are right. I'd even go further: I'd say that doing *anything*
unnecessarily is undesirable in many situations. ;)

And yet you're happy to introduce inheritance all over the place
unnecessarily, it seems, just to avoid static methods.
I've read your interesting article. Please refer to one of my recent
posts here from today regarding how to keep a design stable without
sealing.

You haven't answered most of the problems of inheritance though, nor
have you considered the possibilities of new methods which hide
existing methods instead of overriding them.

I'm still happy with sealed classes, and wish it were the default in
C#.
Again I agree with you. Could you give a quick example of >
representing other behaviour < that's appropriate for this
discussion? I frankly can't see an issue here.

LINQ to Objects is the *obvious* example. The idea of filtering (Where)
Yep, right. Got to hurry over the keys sometimes... Yet, where's the
difference?

One is a technically correct term, the other isn't.
Is this really common? I've never seen some kind of StringHelper
class. Yet I've seen many member functions taking a string object as
parameter without being string itself. Yet, all these functions
didn't change a string's state. In fact they actually can't. But I
know the StringBuilder class which is a perfect example of not using
Extension Methods but a discrete class.

Pretty much every project I've worked on in .NET and Java has had
things like this. StringBuilder is a class which works on a string
repeatedly - I'm talking about methods like "escape with backslashes"
or "encode HTML entities" etc.
Again I'd like to see a short example of how Extension Methods would
to a trick common OOA implementation grounds don't cover gracefully.

With extension methods, I can use LINQ to Objects with any
IEnumerable<T>. With your suggested "solution" I'd have to create a new
instance of some derived class *every time I wanted to do anything*,
completely unnecessarily.
OK, we have a disagreement here. But still you will see that
debugging Extension Methods and maintaining applications using them
will become a big hassle. *That's* in fact the reason for avoiding to
use static methods.

No, I don't see that at all - not when they're used judiciously. If
they're used all the time, completely arbitrarily, they'll cause issues
- but the same is true for anything (and particularly true for
inheritance, by the way - the deeper the inheritance hierarchy, the
harder debugging is IME).
 
A

Axel Dahmen

For example, Marc
Gravell and I recently wrote a "push" version of LINQ by implementing
the same set of operators as extensions on a different interface.

I've got to go to my job... Just a quick note on this: So you've actually *written* extensions yourself? So what's the advantage then? Where's the agnostic point? (This might be a stupid quick question. Just wanted to give it before I leave home..)

Regards,
Axel
 
J

Jon Skeet [C# MVP]

Axel Dahmen said:
I've got to go to my job... Just a quick note on this: So you've
actually *written* extensions yourself? So what's the advantage then?

Our LINQ operators work in a different way, which has advantages in a
few situations, but make life more complicated in others.
Where's the agnostic point? (This might be a stupid quick question.
Just wanted to give it before I leave home..)

The "agnostic" side is that I can still use a C# query expression with
my own LINQ implementation. C# doesn't care where the
Select/Where/SelectMany etc methods come from.

See
http://msmvps.com/blogs/jon.skeet/archive/2008/01/04/quot-push-quot-
linq-revisited-next-attempt-at-an-explanation.aspx

for more on this.
 
J

Jon Skeet [C# MVP]

Axel Dahmen said:
You are not talking about Extension Methods now anymore, are you?

Um, yes.
You are talking about Implicitly Typed Local Variables in C# 3.0, not
Extension Methods. Let's not argue on these please.

I never mentioned implicitly typed local variables. Where did you get
the idea that I was talking about them?
 
J

Jon Skeet [C# MVP]

Axel Dahmen said:
Wow, you're quick... ;)

Are we still discussing Extension Methods here? Or are we discussing
LinQ? I'm still sure that everything Extension Methods did to LinQ
could have been accomplished without them as gracefully.

Without extension methods (and without actually putting all the LINQ
query operators as part of IEnumerable<T> itself, which would be a
crazy burden on implementors) you couldn't write:

mySource.Where(x => x.Length > 5)
.Select(x.ToUpper())

(and all the other query operators, of course)

with mySource as any of:

Stack<string>
string[]
List<string>
or basically *any* IEnumerable<string>

The ability to work on *any* IEnumerable<T> without having to create an
intermediate wrapper class or something similar is part of the elegance
of LINQ in my view.
 
A

Axel Dahmen

Without extension methods (and without actually putting all the LINQ
query operators as part of IEnumerable<T> itself, which would be a
crazy burden on implementors) you couldn't write:

mySource.Where(x => x.Length > 5)
.Select(x.ToUpper())

(and all the other query operators, of course)

with mySource as any of:

Stack<string>
string[]
List<string>
or basically *any* IEnumerable<string>

The ability to work on *any* IEnumerable<T> without having to create an
intermediate wrapper class or something similar is part of the elegance
of LINQ in my view.

Frankly, I don't see a difference to my example, where you just would had to write:

myLinq.Where(x => x.Length > 5)
.Select(x.ToUpper())

No IEnumerable<T> extension, no wrapper class. Just a new kind of collection class (MyLinq). Did you have a chance to look at my example? Of course, I didn't implement delegates as method parameters. I was just a quick example. But implementing that would be an easy exercise. And it would have been done my MS, not us.

Regards,
Axel Dahmen
 
J

Jon Skeet [C# MVP]

Axel Dahmen said:
Frankly, I don't see a difference to my example, where you just would had to write:

myLinq.Where(x => x.Length > 5)
.Select(x.ToUpper())

No IEnumerable<T> extension, no wrapper class. Just a new kind of
collection class (MyLinq).

But *every* time I want to use LINQ, I then have to create an instance
of MyLinq - which is adding no value to me. It's just letting me call a
bunch of methods to act on any IEnumerable<T>. Well guess what
extension methods are doing... transparently.

Adding an extra piece of inheritance without actual specialization
doesn't add any value, just complexity in my view. You're just hiding
*effectively* static methods behind instance methods.

Would you be happier if the Math class had to be instantiated each time
you used it, too?
Did you have a chance to look at my
example?

I looked at your example which copied all the data every time, instead
of streaming it. Do you have another one? Of course, it's possible to
wrap that behaviour as well, but I do think the fact that you don't
grasp the streaming and deferred nature of LINQ as being important
shows that you're not really tuned into it yet.
Of course, I didn't implement delegates as method
parameters. I was just a quick example. But implementing that would
be an easy exercise. And it would have been done my MS, not us.

Actually, LINQ to Objects is pretty straightforward to implement for
the most part - certainly if you don't need a particularly efficient
OrderBy algorithm, for example.
 
A

Axel Dahmen

Hi Jon,

I've just re-read the last two replies on this. Well, I didn't seem to get your point because my former reply was actually wrong, I'm afraid.

Let me correct it:

If I follow my example, MyLinQ wouldn't actually derive from any other class. MyLinq<T> is initialized by using a collection. And that collection becomes a *member* of MyLinQ. So there is no inheritance involved.

So, two implicit conversion operators should do the trick:

class MyLinq<T> where T:class
{
...

public static implicit operator <T>(MyLinq<T> myLinq)
{
return myLinq._coll;
}

public static implicit operator MyLinq<T>(<T> myColl)
{
return new MyLinq<T>(myColl);
}
}


So now in your own code you could write:

static void Main(string[] args)
{
Queue<Person> persons = new Queue<Person>();

persons.Enqueue(new Person { FirstName = "Peter", LastName = "Ustinov" });
persons.Enqueue(new Person { FirstName = "Bart", LastName = "Simpson" });


Console.WriteLine(MyLinq<Person>(persons).Select().Count());
}

One single line... Clean, easy, simple and obvious to everyone working on the project. No Extension Methods required here.

Regards,
Axel Dahmen
 
A

Axel Dahmen

But *every* time I want to use LINQ, I then have to create an instance
of MyLinq - which is adding no value to me. It's just letting me call a
bunch of methods to act on any IEnumerable<T>. Well guess what
extension methods are doing... transparently.

Exactly. Please refer to my reply below which comes without the constructor call. Yet I'd prefer to have that constructor call. It keeps the programmer bear in mind that a new object is being created. For subsequent calls the object should be cached.

Adding an extra piece of inheritance without actual specialization
doesn't add any value, just complexity in my view. You're just hiding
*effectively* static methods behind instance methods.

Adding methods is in fact a kind of specialization. On the fly no other analogy comes to my mind than this, rather depicting, one:

A dog that is able to fly, say 100m, is a specialization of a standard dog. No new property, just a new member function [myDog.fly(100);]. It's still a dog, but a special one... Having another, standard, dog fly 100m by, say, using a catapult will not make that dog a specialized one. It's still a standard dog that's flying. And people should see that you're using a catapult to make him fly. And it shouldn't be made too easy to use these catapults to make a normal dog fly like a flying dog. Although both look the same while flying, the result, when they come down again in the end, will be different when it comes to maintaining/debugging the dogs.

Would you be happier if the Math class had to be instantiated each time
you used it, too?

Well, you don't write "Math.Plus(a, b);", you write "a + b".
I looked at your example which copied all the data every time, instead
of streaming it. Do you have another one? Of course, it's possible to
wrap that behaviour as well, but I do think the fact that you don't
grasp the streaming and deferred nature of LINQ as being important
shows that you're not really tuned into it yet.

You're absolutely right. I don't know about LinQ yet (that's why I regretfully need to leave this thread in order to return learning) and I copied the collection in my example. But please keep in mind that I hacked it in in just two or three minutes. Just put the actual LinQ Extension Method bodies in there and everything will be as with LinQ which does all the same on the first Extension Method argument.

Actually, LINQ to Objects is pretty straightforward to implement for
the most part - certainly if you don't need a particularly efficient
OrderBy algorithm, for example.

I'm looking forward to read into it!

Actually, as I just wrote, I regretfully need to leave this thread in order to continue learning .NET 3.5. Perhaps I'll revisit it again over the weekend, but that will be the final for me.

Thanks, Jon, for taking the time and arguing with me. Talking to you is always a pleasure.

Best regards,
www.axeldahmen.com
Axel Dahmen
 
J

Jon Skeet [C# MVP]

Axel Dahmen said:
I've just re-read the last two replies on this. Well, I didn't seem
to get your point because my former reply was actually wrong, I'm
afraid.

Let me correct it:

If I follow my example, MyLinQ wouldn't actually derive from any
other class. MyLinq<T> is initialized by using a collection. And that
collection becomes a *member* of MyLinQ. So there is no inheritance
involved.

But there's still an extra class for no good purpose, which makes using
LINQ to Objects more tedious apart from the implicit conversions -
which you seem to think are *more* readable than extension methods.

Oh, and <T> isn't a type. Did you mean IEnumerable<T>?

A wrapper which solely exists to provide methods which always (at least
hopefully) always a new thing seems pointless when you can (with
extension methods) effectively add methods to act directly on the
original object.
One single line... Clean, easy, simple and obvious to everyone
working on the project. No Extension Methods required here.

No, just implicit conversions, invalid syntax, and still not fixing the
deferred and streaming issues.

Nope, I still think MS took the right course of action.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top