Oh Jeff… now even you’re referring to all .NET features such as extension methods as C#. You’ve obviously lost your self-claimed agnostic point of view.
While I enjoy the additive nature of C# extensions…
Ya know, you coulda just said …nature of .NET extensions…
Not to pick you know what off the wall, but if you’re going to use a VB function such as Left as your example, at least give the lang a little love brotha… Pffft.
Jeff,
I just wanted to make an observation about monkeypatching, Avdi Grimm’s experience, your earlier posting on unit testing, and your earlier posting on subversion. I think that Advi could have saved himself a lot of grief if he had some way to do regression testing, i.e., if his code was under version control and he evaluated it at different versions until he found the first location where it broke to see what the change was and why it broke.
I’m assuming of course, that Advi had access to all of the code and that all of the code was under version control. If the problem child in the ruby control was not under his availability to inspect, well, then, in so many words… he was royally screwed.
After having to deal with other peoples’ code I’ve become a True Believer in version control, first with subversion, and then with git. (I love being able to access subversion repositories using git-svn, a git client that checks out from subversion into my local git repository where I do all of my experimentation/development and then push out the history of changes back to the subversion repository with a single git-svn dcommit). If you can’t track the changes in the code you’re using, then life is soooo much harder when finding regression bugs.
That code doesn’t actually exist; it’s only used as an example. I do agree with all the amendments / suggestions people have made, but critiquing dummy code is a little beside the point don’t you agree?
I don’t agree
One of my few gripes about Coding Horror is the lack of actual code, so it’s nice to see some. Won’t Stack Overflow be all about voting on code snippets?
Also, as someone who has never used C#, I’ve learned in just a few lines: 1) it does look like Java 2) but it’s more powerful 3) you can use var 4) you can have free functions 5) the extension syntax is clean and 6) what code in a real C# codebase might look like.
I’ve only started learning C# for a few months now, but one of the most annoying things I find about it are these ambiguous method names. I mostly program in Java and C++, and I have no background in BASIC, so maybe I’m just not used to it, but the method name Left has no meaning to me. Maybe, getLeftSubString. I dunno, maybe I’m crazy.
Personally I’m fine with Prototype.js (or other framework such as MooTools)… Some might consider it monkeypatching (and I guess it is since they both redefine native objects etc…), but at least both framework are well-documented and adds a lot of functionality to javascript…If you decided to work with Prototype or Mootools, you pretty much accept that they both redefine native object as it’s pretty obvious to anyone who spends more than 2 minutes looking at the code or the doc… The lack of documentation is what makes monkeypatching hard to bare with imho…
Interesting. Is it the OO exposure that makes people prefer (sometimes strongly)
s = s.Left(5);
to the much more functional (and to me, sane)
s = Left(s, 5);
? If you are going to use function overloading on that, you really should have derived your own StringEx class for ease of understanding. I do agree that it makes applying test harnesses significantly easier, though.
That code doesn’t actually exist; it’s only used as an example. I do agree with all the amendments / suggestions people have made, but critiquing dummy code is a little beside the point don’t you agree?
As you said and well the problem isn’t giving the programmers the ability to override a core class, but the programmers that do it. In Lisp you can do pretty much whatever you want to to the core language but you should still adhere to conventions for sanity sake. I spend my days in ActionScript 3, and it might come as a shock to some of you it’s actually a very evolved language, and altough, like c# it only allows to add, not override, methods, i prefer to either wrap the core class or to have a utility class.
This is because it’s a) easier to document and mantain and b) because it gives the user(programmer) the choice to use it or not.
I think that in it’s essence it’s a bad practice to add to core classes as it also may result in conflicts with future language updates, what was to happen if in the next version a Left method was to be implemented, that said I much prefer either a wrapper or utility class.
I know this post is really about monkeypatching but I’m going to go off at a tangent anyway because the first part of the post reminded me of a conversation we were having at work last week.
What I’m going to talk about is example code. Now it’s not a critique of the code shown itself, that’s a function it does a job that I understand. It’s to do with code snippets being shown without scope or context, or to put it another way what’s not shown.
For example your extension function should be defined in a non-nested, non-generic static class (according to msdn - I’ve not had chance to use them much myself), and bought into scope with the using directive if not in the current namespace. But without this small but important bit of information it could leave some people scratching their heads over where this function should actually go - should it be a partial class of the string object, does it have to be named in a particular way, or located in a particular place.
I hope this is something people will be thoughtful of when posting code in future as it really can reduce the helpfulness of well intended examples. Perhaps it’s something you may like to try and educate people to do when they eventually start posting their examples onto stackoverflow.com
I’ve seen this in other languages although I’ve never actually ‘monkeypatched’ myself (although I have written a fair number of operator functions in C++ classes).
My question is: what happens in case of a collision? Let’s say you define String.Left in one namespace, and then import another namespace that also has defined String.Left. Which takes precendence? Will the C# compiler catch you? Will it complain? Or will it just choose the last String.Left method it imported?
I’d like to share some practical uses of monkey patching.
‘hot patch’ of libraries being old, buggy or lacking a feature
For instance, I’m using platypus ( subpackage of reportlab ) to generate PDF documents in python. The problem with this library is the use of a global instance, making it buggy when generating multiple documents in multiple threads. I’ve overridden a few methods to add a locking mechanism. Essentially, this makes the deployment easier ; I don’t have to provide a patch for the platypus library along with my application, making it a deployment nightmare. Patch is made at runtime, and the admins on the production server just have to make sure the standard reportlab packages are installed, not a patched release that might interfer with other applications.
Another example is a cherrypy monkeypatch I use in a turbogears application, allowing to check upload status : http://www.cherrypy.org/attachment/ticket/546/uploadfilter.py . I hacked it so it checks the content-type of an uploaded file before continuing the upload. I don’t want to provide a patched cherrypy either, I just monkeypatch it at runtime.
These monkeypatches enable to patch libraries without having to provide a patch along with the application, avoiding deployment headaches.
I’m just in love with RoR duration monkeypatches, allowing such things as Table.count(:conditions = [created_at ?,24.hours.ago]). For sure, it’s more evil as it implies changes on builtin types, reminding us those overloaded operators nightmares in C++. But it’s giving us incredibly short and readable code !
Ya know, you coulda just said …nature of .NET extensions…
It’s still a specific language feature, not a .NET feature. Nothing in the rest of the architecture other than the language compiler need change for the feature. Sure he should perhaps have said C# and VB.NET extensions, but definitely not .NET extensions
Just write real abstract dummy code then instead of C#.
It’s quite difficult to write abstract dummy code when talking about a specific language feature.
@vincent: Sure doing it at runtime sounds excelent, but it just moves the problem further down the line, what happens if a developer inherits your code and applies it at compile time? or if it becomes a part of the core class?
@Powerlord - C# Extensions are just a compiler shortcut. The method has no access to the internals of the extended class, and the behavior can not be unintentionally called unless the source ‘uses’ the namespace+assembly that the extension is defined in.
Please note I’m not a fan of Spartan Programming, and I’m shortening to ‘p’ only for blog readability
e.g. We use a vendors point class, and need to compute area, but the vendor doesn’t provide it. I’m assuming we don’t have control of the point class, otherwise we’d just change the class.
public class Point { public double A { get; set; } public double B { get; set; } }
Note that the following two methods are semantically identical, as is the calling IL.
// a point class, lacking a useful area method.
public sealed class Point { public double A { get; set; } public double B { get; set; } }
public static class PointExtension
{
// old way
public static double PointArea(Point p) { return p.A * p.B; }
// new way
public static double Area(this Point p) { return p.A * p.B; }
}
public class Program
{
public static void Main()
{
Point point = new Point() { A = 2.2, B = 1.1 };
double areaOldWay = PointExtension.PointArea(point);
double areaNewWay = point.Area(); // same as old way, just looks nicer.
}
}
Note that if a name conflict occurs (i.e. Point later defines Area() method), the class implementation gets preference, and the compiler (currently) doesn’t even warn you that your extension method will never get called.
All in all it’s a nice language addition when you don’t want full blown containment / subclassing.
Actually you can monkeypatch in Obj-C as well. The language itself doesn’t allow it (it only allows to add functions to a class dynamically), but since everything is very dynamic in Obj-C (much less is static as in C++), you can pretty much modify whatever you want, even things you are not supposed to modify.
But there is something about monkeypatching you missed completely in your post. Sometimes it is necessary to make software work correctly. There are situations where the real implementation is buggy and while you could subclass and use your subclass only, if you offer a plugin/framework/library for other people to use, you can tell them Please don’t use class XYZ, but instead our subclass of it, as the method ABC of XYZ is buggy, you can’t force them. By monkeypatching the broken method, you kind of fix someone else’s bug, which is sometimes really useful and just makes things work.
The problem with it that one day the developer of this class sees the bug and fixes and, then you monkeypatch a method that isn’t broken any longer. However, in a dynamic language this is no big deal, as there you can check the version of a plugin/lib/framework and depending on outcome you either monkeypatch the method or you don’t.
@pedro, if I get your point, if I find a compiler for ruby or python, every method or package member would be virtual, and thus, overridable. For sure it’s meant only for dynamic languages. For statically ( and thus faster ) compiled languages one would have to fallback to more classical approaches : either provide a patched library, or hide the whole buggy library behind an adapter interface which cares of the workaround( provided the monkeypatch is used for fixing an existing library ). I see it as a cheap shortcut which has to be used wisely. If I’d needed absolute security at any price, I’d avoid buggy library or would debug it thoroughly, indeed.