Leaky FunctionsBarrel of Bugs
Jomo Fisher--Pop quiz. Consider this function call in C#:
a = MyFunction(b);
What information is exchanged between the caller and the function? When is the information exchange done?
It would be nice if the answer was:
MyFunction takes value b and returns value a. When the function finishes no more information is exchanged.
There are several reasons information may leak out of a function call and all of these reasons can cause subtle bugs in your code.
Let me start with the granddaddy of function leaks to illustrate my point:
static int counter = 0;
static int MyFunction(int v) {
++counter;
return v;
}
This is good old-fashioned global data. I think it’s well accepted that globals can cause bugs so I won’t go into that. My point is that there is a family of function leaks and to a greater or lesser degree all these leaks can cause subtle bugs in your code.
Here are some more leaks that have personally caused me grief in the past.
Reference Parameter as Value Assumption
Here, I want to pass a point to a function.
Point pt = new Point();
pt.X = 1; pt.Y = 1;
SetPixel(pt);
class Point { public int X; public int Y; }
static void SetPixel(Point pt) {
// Do some work...
pt.X = 0;
}
Did you see that? Whoever wrote SetPixel decided they could change my Point instance behind the scenes.
Well, at least the damage is done once the call to SetPixel is complete. There’s a good chance of finding this bug quickly.
What about this variation?
class Graphics {
Point lastPixelSet;
public void SetPixel(Point pt) {
// Do some work...
lastPixelSet = pt;
}
}
Now SetPixel is holding my point and can change it even after SetPixel has returned.
Collection Parameter as Value Assumption
Things get worse for collections:
static void WriteList(List<string> list) {
// Do some work.
list[5] = "I like ponies.";
}
You could try changing the signature to take IEnumerable, but trouble can find a way:
static void WriteList(IEnumerable<string> list) {
// Do some work.
if (list is List<string>)
((List<string>)list)[5] = "I like ponies.";
}
Return Reference Leaks
Function callers can also cause problems:
Optimizer o = new Optimizer();
Point p = o.FindMaxima();
p.X = 0; // Yikes! This changes private data held by Optimizer.
class Optimizer {
private Point maxima;
public Point FindMaxima() {
return maxima;
}
}
A Patchwork of Fixes
So we’ve identified a coding pattern that can cause subtle bugs, is there anything we can do? One approach people take is to copy structures at function call and return boundaries:
SetPixel(pt.Duplicate());
class Point {
public int X;
public int Y;
public Point Duplicate() {
Point d = new Point();
d.X = X;
d.Y = Y;
}
}
There are some problems with this. First, who is responsible for making the copy? Is it the caller or the function? If you don’t have a rigorous rule for your code then often you end up with two copies or no copy at all. Second, all those Duplicate calls clutter your code and obscure its intention. This is a maintenance problem.
For collections, there’s a built-in solution that will let you wrap your collection in a read-only façade:
List<string> contacts;
IEnumerable<string> GetContacts() {
return contacts.AsReadOnly();
}
This is well enough if you remember to do it. You could do a similar thing for your own non-collection classes, but then every class would need another separate class to act as the read-only reference.
An Elegant Solution
You can find a far more elegant solution in the .NET String class. Try this:
string s = "abcde";
DoSomething(s);
static void DoSomething(string s) {
s[3] = 'x';
}
Does this modify the string from the calling function? The answer is no. In fact, this won’t even compile because there’s no setter implementation on the indexer. This is important:
There’s no way at all to change a .NET string once it’s been created.
The string class is immutable. You can verify yourself by right-clicking on the string and selecting Goto Definition.
This means that any function that takes a string as a parameter or returns a string has an explicit contract that the function can’t leak through that string reference.
An Immutable Point
Here is the Point class implemented as immutable:
class Point {
public int X {get; private set;}
public int Y {get; private set;}
public Point(int x, int y) {
X = x;
Y = y;
}
public Point SetX(int x) {
return new Point(x, this.Y);
}
public Point SetY(int y) {
return new Point(this.X, y);
}
}
Now, you’re free to pass these Points around without copying them and without worrying about leaky functions. It’s true that you have to copy the point to change it, but I think the fact that string is a successful immutable class means that other immutable classes can also be successful.
I have a similar immutable list implementation which I will save for a future blog entry.
This posting is provided "AS IS" with no warranties, and confers no rights.
Comments
Anonymous
May 16, 2007
<quote>There’s no way at all to change a .NET string once it’s been created.</quote> don't forget about reflection. http://blogs.tedneward.com/CommentView,guid,1490880b-832f-402e-9b18-4c101c4a5206.aspx messing around with interned strings is a great way to foul up almost any .net programAnonymous
May 17, 2007
Even as I wrote it I knew using the words "no way at all" was probably asking for trouble. I did know about the unsafe technique but I wasn't aware of the reflection method. I stand corrected. Still, strings protect you against all but the gnarliest opponent (err, I mean co-worker).Anonymous
May 21, 2007
C# and .NET need a proper, integrated, and wholistic treatment of immutable values - including support for the property initializer syntax.Anonymous
May 23, 2007
Jomo Fisher --I recently got a question via my blog that dovetailed nicely with something I’ve been workingAnonymous
May 24, 2007
You've been kicked (a good thing) - Trackback from DotNetKicks.comAnonymous
May 24, 2007
The comment has been removedAnonymous
May 29, 2007
Good article and grats for the blog! :)Anonymous
July 23, 2007
Jomo Fisher—A lot of people (myself included) have written about LINQ in the next version of C#. LINQAnonymous
August 28, 2007
Nice post. I think, though, that using Point for the examples is not a good idea because System.Drawing.Point is a value type, so the bugs mentioned here would not occur when using System.Drawing.Point.Anonymous
May 09, 2008
Now that we have covered the basics, in minutes 8 - 14 we will cover the foundational concepts and typesAnonymous
July 10, 2008
Lists represent the backbone of functional programming and in order to be an effective F# programmerAnonymous
August 09, 2008
在上篇文章里,我们写出了F#的第一个程序,本文我们来看一些F#语言的核心部分,包括值的不变性,模块,Tuple,柯里化,Union类型,模式匹配,Record类型,序列和集合等内容,读完此文后,希望能让您对F#有个整体的认识。Anonymous
September 26, 2008
I've been complaining about many of these aspects of .NET for a long time. But F# and immutable values is not the only solution. Many of these problems could be solved in C# and OO language by enabling a feature that C++ had, and used for many years... const! You could pass values into a function as const reference, and you could also declare methods (or properties) as const, and the compiler would check to make sure that this method did not change any internal variables. By passing references around everywhere in .NET, we have create many problems that were once solved.Anonymous
October 16, 2008
原文链接:http://www.cnblogs.com/anderslly/archive/2008/08/10/fs-in-20-minutes-core.htmlAnonymous
December 29, 2008
OMG! I got pulled off my usual job of doing a bunch of things and have been focused on getting studentsAnonymous
May 26, 2009
第一篇,从零开始编写我们的第一个F#程序。 什么是F#,我为何要学它? F#是一种.NET平台上的 函数式编程 语言。就像C#和VB.NET,F#可以利用.NET的核心类库,如 WPF , WCF ,