Sunday, 12 September 2010

Construct something else! (C#)

Please read my follow-up post after reading this one.

Quoth rjw on stackoverflow...
Given the following client code:
    var obj = new Class1();
Is there any way to modify the constructor of Class1 so that it will actually return a subclass (or some other alternate implementation) instead?

C# compiler guru, Eric Lippert commented...
We are considering adding a feature "extension new" which would essentially allow you to make a static factory method that would be called when the "new" operator is used, much as extension methods are called when the "." operator is used. It would be a nice syntactic sugar for the factory. If you have a really awesome scenario where this sort of pattern would be useful, I'd love to see an example.
I have one!

Version one of our DLL had a class that wrapped a connection to a remote server.

    using (var connect = new ExampleConnection("service.example.com"))
    {
        connect.DoStuff(42);
    }

It worked great. Our customers were very happy with it and developed lots of code to use our little DLL. Life was good.

Time passes and our customers ask us to add support for a different type of server that does a similar job but with a very different protocol. No problem, we develop a new class called DifferentConnection and just to be helpful, both ExampleConnection and DifferentConnection implement a common interface object.

We're about to release version two to our customers, but a common response comes back;
"This is good, but we were hoping your library would automatically detect which variety of server it's talking to. Also, we really don't want to change our code. We want to just drop your updated DLL into the install folder, but we'll recompile our EXE if we really have to."

With these new requirements, ExampleConnection had to become a class that supported both varieties of remote server. The constructor has to perform the auto-detect, and all of the public functions now all begin with an if statement, selecting for which variety of remote server is in use.

If we had a bit more foresight, we should have supplied a static Connect function that wrapped a private constructor. That way, version two of this function could have returned a subclass object instead. But we didn't. There are costs to writing code that way, so you wouldn't do it unless there was a clear point to it. If a normal constructor could return a subclass instead, there would be no problem.

Mr Lippert, I hope this provides the justification you need to add this to .NET 5, but I'd much rather have destructors on structs instead. I also want a pony.

Picture credit: 'LEGO Mini Construction Site' by flickr user 'bucklava'.
(I don't really want a pony.)

UPDATE: Someone submitted this to reddit. Lots of discussion there.
UPDATE(2): Follow-up post.

14 comments:

  1. Connection.CreateConnection("service.example.com") is the way to go here. If you anticipate needs like this, put it in the first version. If you need to make a change like this, asking them to recompile is really the way to go.

    The proposed change causes a lot of code reading/static analysis/debugging confusion for next to no gain.

    ReplyDelete
  2. sounds like your interface is failing to provide enough coverage ...

    ReplyDelete
  3. Even if the C# team do include an 'extension new' syntax, if v1 of your DLL had a standard constructor, and customer has a DLL compiled against v1, if you ship v2 with no standard constructor, only extension new methods, your customer's DLL is going to need a recompile anyway. They may just not have to make any source changes to take advantage. If they continue to use the old binary, it'll fail when it tries to call a constructor that's no longer there.

    It's like if v1 of your code had a public field called Foo, and in v2 you replaced it with a public property called Foo - client code will be source-compatible with the change, but they'll have to recompile - it won't be binary compatible.

    ReplyDelete
  4. I have the same thought many times before. It seems that the constructor is a very special case of a static method. Consider the following:

    class A {
    public A(){ ... }
    public static A Construct() { ... }
    }

    - The static method can return null while the constructor cannot.

    - The static method can return an object of a subclass while the constructor cannot.

    ReplyDelete
  5. One big limitation of pseudo-new compared to real-new is that you couldn't have a subclass's constructor base itself on a pseudo-new.

    ReplyDelete
  6. I think it would be nice as a way of mocking out classes for testing, but in production code, it would be terribly deceptive.

    ReplyDelete
  7. Please, C# team, don't add this.

    Wilhelm, use a static factory method, or make your ExampleConnection class a wrapper which delegates to the right strategy ( http://en.wikipedia.org/wiki/Strategy_pattern ) for the connection

    ReplyDelete
  8. C# team, please do not add this misfeature.

    Wilhelm, either use a static factory method, or make your ExampleConnection class a wrapper that delegates to the strategy for each kind of connection as per http://en.wikipedia.org/wiki/Strategy_pattern

    This will remove the need for an if statement in each method. Don't add confusing features to the language when the problem can be fixed without that.

    ReplyDelete
  9. Hindsight is always 20-20 ;)

    When you cross a boundary use an interface (for the client to develop against).
    In this case I think with using the interface you also naturally use a static factory method.

    And voila, the problem would never have existed. ;)

    ReplyDelete
  10. Design patterns exist because of deficiencies in a language for real-world usage.

    One of the real good things about C# is that it seems the design team has, over time, taken a look at common design patterns and given them a better treatment by making them part of the language itself. Things like properties and events -- these require patterns in Java, yet they exist as first-class features of C#.

    Given the prevalence of things like the Strategy Pattern, or the Factory Pattern; it seems like a reasonable thing to look at the underlying cause of the need for those patterns -- which is that the object creation process (the "new" keyword) isn't user-extensible. It should be. I'd even argue that instead of making 'extension new' functionality a feature of C#, bake it right into the CLR and allow MSIL's newobj opcode to automatically thunk over to an alternate static factory method based on whether an attribute exists at runtime or not so that all languages on the CLR can benefit from it.

    ReplyDelete
  11. This is why you should always be using a inversion of control container (StructureMap is brilliant on .NET) to create dependencies, instead of ever calling a constructor yourself.

    Had you done this, changing the actual constructed type would have been a simple configuration change.

    Plus, you are now decoupled, and could write MockConnection, and easily create unit tests.

    ReplyDelete
  12. Instead of craziness like this, please include const types and methods like in C++

    ReplyDelete
  13. Or consider a pure OO language where classes are objects... It's turtles all the way down :)

    ReplyDelete
  14. Sounds like a good use for Dependency Injection. Sprint.NET does this.
    http://www.springframework.net/

    ReplyDelete