views:

59

answers:

2

I understand that I can't extend static classes in C#, I don't really understand the reason why, but I do understand that it can't be done.

So, with that in mind, here is what I wanted to achieve:

public static class GenericXmlSerialisationExtender
{
    public static void WriteToXML<T>(this T targetObject, string fileName)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        using (TextWriter writer = new StreamWriter(fileName, false, Encoding.UTF8))
        {
            serializer.Serialize(writer, targetObject);
        }
    }

    public static T ReadFromXML<T>(string fileName)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        using (TextReader reader = new StreamReader(fileName, Encoding.UTF8))
        {
            return (T)serializer.Deserialize(reader);
        }
    }
}

I.e. I wanted to define .WriteToXML for instances (there are quite a lot of config / static data classes which I need to use which just use vanilla XML Serialization), and then .ReadFromXML for types.

So effectively I could call something like:

MyType typeInstance = MyType.ReadFromXML(path_to_data);

What would be the 'proper' way of encapsulating that? I once worked with a colleague who believed 'code re-use' was copy & paste, and I'd rather not put myself in that bracket!

+1  A: 

I'm not actually recommending this, but as an option:

public static T ReadFromXML<T>(this T ignored, string fileName)
{
    XmlSerializer serializer = new XmlSerializer(typeof(T));
    using (TextReader reader = new StreamReader(fileName, Encoding.UTF8))
    {
        return (T)serializer.Deserialize(reader);
    }
}

Call it with:

MyType typeInstance = default(MyType).ReadFromXml(path_to_data);

It's pretty horrible, but it's one way round the problem to some extent.

I suspect I'd actually just suck it up and do:

MyType typeInstance = GenericXmlSerialisationExtender.ReadFromXml<MyType>(...);

If you can give GenericXmlSerialisationExtender a snappier name, that shouldn't be too painful.

Jon Skeet
Thanks... Can you shed any light on why the compiler couldn't just expand it out to the way you would actually do it (which is what I expected it would do)? Just for my understanding really...
Matt Whitfield
@Matt: You're trying to add a sort of inheritance that just doesn't exist, really. I dare say the language could have been designed along these lines, but it would probably add extra complexity in various places.
Jon Skeet
Jon - thanks - can you recommend any good reading in terms of finding out exactly why it couldn't be supported? Because as far as I understand it, all the elements required to support the syntax are there already... So I must be missing something, I guess.
Matt Whitfield
@Matt: There doesn't tend to be much documentation around why features *aren't* implemented. Eric Lippert is fond of talking about the costs of designing, implementing and testing features - perhaps kind of inheritance just hasn't been seen to be compelling enough?
Jon Skeet
Jon - thanks, I think that gives me a good enough understanding!
Matt Whitfield
Eric is actually quite active here on SO (http://stackoverflow.com/users/88656/eric-lippert). Perhaps if you're really (really) lucky and he spots this he'll make some comments.
Simon P Stevens
+2  A: 

You would define it in exactly the way you have done already, but to call it you would use a standard static method call:

MyClass deserializedObject = GenericXmlSerialisationExtender.ReadFromXML<MyClass>(@"c:\filename.xml");

(You might want to give your GenericXmlSerialisationExtender class a more appropriate name if you do this, or move it to a different static class)


The reason that extension methods can't work on Static methods, is that there is no object to attach the extension method too.

In your example:

public static T ReadFromXML<T>(string fileName)

Nowhere in that line have you defined the type that you would like to extend. Extension methods require that the first parameter be an object that you would like the extension method to act upon.

Extension methods are just syntactic sugar, as a nice way of creating a static helper method.

in .net 2.0 you would write:

public static class StringHelper
{
    public static String AddFullStop(String data)
    {
        return data + ".";
    }
}

String input = "test";
String output = StringHelper.AddFullStop(input);

.net 3.5 gives you the ability to do this:

public static class StringExtensions
{
    public static String AddFullStop(this String data)
    {
        return data + ".";
    }
}

String input = "test";
String output = input.AddFullStop();

So when you want to do something outside the scope of the handy extension methods, you just fall back to the older static helper method pattern.

Simon P Stevens
Where you say "is that there is no object to attach the extension method to" - that is the bit that I *don't* understand. Why would it be a really massive leap to just expand it out to the code that you suggested? Not being argumentative, just want to understand actually *why*...
Matt Whitfield
@Matt: Because you haven't specified the "type" that you want to extend in the extension method signature. Which type would the extension method appear on? (It's all very well that in your case you actually just want to extend "Object", but that won't always be the case, how would you tell the compiler that you wanted your static extension method to appear on Strings). It would require the C# design team to add more syntax changes to allow you to specify which type you wanted to statically extend.
Simon P Stevens
I suppose the C# team could implement that new syntax, but it's a pretty unusual case. Extension methods were added to support the very common "static helper class" pattern. They don't generally make syntax changes just to support unusual edge cases like the one you are suggesting. It also doesn't actually gain you anything in readability, there isn't much difference between writing String.HelperMethod(); or StringHelper.Method();
Simon P Stevens
@Simon - surely if you wanted to restrict the types on which the method was available you could just use the standard, and already defined where constraint - so, for Strings, just use `where T: String`?
Matt Whitfield
@Matt: "where T: String" is invalid - "string is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter." - Also you're assuming that your proposed "static extension" is generic. It wouldn have to be with your proposed syntax. There are restrictions on generics like not being able to call methods not defined in the constraint interface, that would cause many other problems. Finally, generic constraints in that form, only constrain that the type must inherit from String (if that was even allowed), which wouldn't be enough in all cases.
Simon P Stevens
@Simon - thanks for spending the time to educate - it's much appreciated!
Matt Whitfield
@Matt: No problem. When it comes down to not implemented features, the common answer is quite often "it is far far more complex that initially imagined". There's probably even more depth to it than we've mentioned so far.
Simon P Stevens