Static generic extension method

S

Steffen Bobek

Extension methods are made for use with instances. I'd like to "misuse" them
as static methods, too. Let me tell you my ambition:

I use an extension method to serialize objects somehow like this:

MyObject obj = new MyObject();
obj.ToXmlFile("some directory\\some file.xml");

The method "ToXmlFile" is defined elsewhere far apart from MyObject to work
for all objects:

public static void ToXmlFile<T>(this T obj, string path)
{
//do the right thing
}

And of course there is another method being a "classic" static method that
reads the XML file and deserializes its contents:

public static T FromXmlFile<T>(string path)
{
//do the right thing the other way round
}

That method is called:

MyObject obj = MyXmlSerializerClass.FromXmlFile<MyObject>("somewhere");

Now my question is: Can you think of a possibility to define "FromXmlFile"
in some way that allows me to call it more convenient? i.e.:

MyObject obj = MyObject.FromXmlFile("somewhere");

A solution that does also work for 3rd party objects whose code I can't
manipulate? i.e.:

TheirObject obj = TheirObject.FromXmlFile("somewhere");

Something one might call "static extension method"?
I'd really appreciate any hints!

Thanks in advance,
Steffen
 
J

Jeroen Mostert

Steffen said:
Extension methods are made for use with instances. I'd like to "misuse" them
as static methods, too.

Short of creating your own C# variant, you can't, end of story.
Let me tell you my ambition:
OK, so maybe it's not.
I use an extension method to serialize objects somehow like this:

MyObject obj = new MyObject();
obj.ToXmlFile("some directory\\some file.xml");

The method "ToXmlFile" is defined elsewhere far apart from MyObject to work
for all objects:

public static void ToXmlFile<T>(this T obj, string path)
{
//do the right thing
}
Hmm. This sounds far inferior to having a class whose job it is to
serialize. Something like, I don't know, XmlSerializer. Static methods are
quite inflexible, and serialization is one of those things where relying on
the One True Approach for everything will probably get you in trouble.

Even if you don't like XmlSerializer, the basic approach of that is sound:
use an object to serialize. Creating an object is more flexible than calling
a static method (as there are more ways of doing the former than doing the
latter). That object can also reuse any run-time structures that need to be
built for serializing a given type. Doing such caching for a static method
will just involve creating similar objects in the background, at increased
bookkeeping overhead.

Aside from that, using an extension method for this just seems wrong.
Extension methods are supposed to provide additional functionality for
specific types. Defining one that works on any T is like extending Object
itself, and that's pretty far-reaching. It makes it harder to reason about
classes and objects in terms of responsibilities: there are no classes or
objects responsible for serialization in your model, it's actually this one
method that's doing the work -- even thought it *appears* as if individual
objects are responsible.
And of course there is another method being a "classic" static method that
reads the XML file and deserializes its contents:

public static T FromXmlFile<T>(string path)
{
//do the right thing the other way round
}

That method is called:

MyObject obj = MyXmlSerializerClass.FromXmlFile<MyObject>("somewhere");

Now my question is: Can you think of a possibility to define "FromXmlFile"
in some way that allows me to call it more convenient? i.e.:

MyObject obj = MyObject.FromXmlFile("somewhere");

A solution that does also work for 3rd party objects whose code I can't
manipulate? i.e.:

TheirObject obj = TheirObject.FromXmlFile("somewhere");

Something one might call "static extension method"?

C# just doesn't support that, and there's really nothing to be gained from
it. Extension methods are a double-edged sword as it is: they're bad for
readability as it's not immediately clear where a method is coming from, and
they're good for readability as they preserve traditional O-O semantics in
absence of developer clairvoyance.

Static methods on classes are not traditional O-O semantics, though, and the
minor readability gain they offer in a scenario like this doesn't weigh
against the drawbacks extension methods have.
 
S

Steffen Bobek

Jeroen, thank you for your reply.

Let me put my approach straight. I think every developer's goal is to write
easily readable and understandable code. And to me this is (amongst others)
excellently done by extension methods, because you write significantly less
code and separate your code by functionality at the same time (in my case
XML serialization is coded in one place only).

Since XmlSerializer itself seems to me like a giant static method (you can
put in any object to have it serialized to XML), I didn't see something
wrong to embed it into an extension method that provides exactly this
funcionality to any object.

My implementation works really fine. It's just the deserialization call that
annoys me, because I imagined the shorter option "AnyObject obj =
AnyObject.FromXmlFile(path)" (to get back to my approach).

But I understand that it's impossible to implement "static extension
methods", sadly. Thanks for your note about that!

Steffen
 
J

Jeroen Mostert

Steffen said:
Let me put my approach straight. I think every developer's goal is to write
easily readable and understandable code. And to me this is (amongst others)
excellently done by extension methods, because you write significantly less
code and separate your code by functionality at the same time (in my case
XML serialization is coded in one place only).
Extension methods aren't all roses and sunshine, though. They also have
drawbacks:

- The former assumption that a call of the form A.B() involves a method .B()
defined either in class A or one of its base classes no longer holds. The
definition of .B() could be anywhere in the set of referenced classes. UI
support is critical.

- To be able to use .B(), you must be aware that it's an extension method,
so you can reference the proper assembly and use the proper namespace. It's
not longer clear what A offers just by reading source code.

- In the same vein, generating useful documentation becomes harder: either
the extension methods are integrated with A (which requires that all known
assemblies offering extensions should have their documentation generated in
one big set) or they're left in the documentation for the extension class
(which makes .B() harder to look up for clients of A).

- The author of A will be unaware of extension methods. If they make a
breaking change, the extension methods may stop working. Of course, the same
would happen to code in a static method, but the nature of the extension
method makes it unclear who is responsible for .B().

Now, these drawbacks are not insurmountable, and the C# designers felt that
the drawbacks were sufficiently offset by the benefits. And the benefits
especially come when you would have to chain multiple of those methods
together, like many of the interesting uses of Linq require. There's a world
of difference between

e.Select(i => i % 2 = 0).Average();

and

Enumerable.Average(Enumerable.Select(e, i => i % 2 = 0));

The latter has the advantage of not having any tricks up its sleeve (you see
exactly what method is called on what class) but in readability the former
clearly wins, as it makes the order of operations correspond with how you
read the expression. On the other hand, the fact that it takes less typing
should not be a deciding factor, IMO.

There is no such benefit to be gained from allowing static methods to be
defined this way, though. Chained calls to those will have the same form. In
fact, for non-generic methods there is no benefit at all, because the only
thing it achieves is a change of class name. This is of no benefit to
readability. It may make your code "look neater", but there is a big gap
between "looking neater" and actually being more readable.

For generic static methods there could be a slight increase in readability
as it obviates the need for repeating the class name as a type parameter,
but generic static methods (other than extension methods, obviously) don't
occur very frequently. This is probably why the C# designers felt the
drawbacks didn't outweigh the benefits.
Since XmlSerializer itself seems to me like a giant static method (you can
put in any object to have it serialized to XML), I didn't see something
wrong to embed it into an extension method that provides exactly this
funcionality to any object.
But XmlSerializer *isn't* a static method. It's an object. I understand you
see it as working like a static method, but it doesn't, not really. The
serialization can be controlled by manipulating properties and you can
attach events for detecting when serialization fails, exactly the sort of
customization that's impossible if you use a static method.

In our particular scenario you don't need any of that and where you will
only ever call .Serialize() and .Deserialize(), but as a general approach
(like the one provided by a library) static methods are usually not good enough.
My implementation works really fine. It's just the deserialization call that
annoys me, because I imagined the shorter option "AnyObject obj =
AnyObject.FromXmlFile(path)" (to get back to my approach).

But I understand that it's impossible to implement "static extension
methods", sadly. Thanks for your note about that!
You could petition the C# designers, but for exactly the reasons I mentioned
above I don't think they'll include static extension methods in future
versions of C#. There isn't enough need.
 
A

Arne Vajhøj

Jeroen said:
Extension methods aren't all roses and sunshine, though. They also have
drawbacks:

- The former assumption that a call of the form A.B() involves a method
.B() defined either in class A or one of its base classes no longer
holds. The definition of .B() could be anywhere in the set of referenced
classes. UI support is critical.

- To be able to use .B(), you must be aware that it's an extension
method, so you can reference the proper assembly and use the proper
namespace. It's not longer clear what A offers just by reading source code.

- In the same vein, generating useful documentation becomes harder:
either the extension methods are integrated with A (which requires that
all known assemblies offering extensions should have their documentation
generated in one big set) or they're left in the documentation for the
extension class (which makes .B() harder to look up for clients of A).

- The author of A will be unaware of extension methods. If they make a
breaking change, the extension methods may stop working. Of course, the
same would happen to code in a static method, but the nature of the
extension method makes it unclear who is responsible for .B().

In my opinion extensions methods are like operator overloads.

Very valuable in the few cases where it makes sense.

Should not be used in most cases.

Arne
 
A

Anders Borum

Hi!
In my opinion extensions methods are like operator overloads.
Very valuable in the few cases where it makes sense.
Should not be used in most cases.

I completely agree. And although some voice that writing less code is not
that important, I feel that it leads to more readable and elegant code. And
when extending standard types from the BCL (such as XmlNode etc.), it's
really not that hard to determine, whether a method came from the actual
type or an extension.

Use where appropriate, not just because it's possible.
 

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