Partager via


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.

kick it on DotNetKicks.com

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 program

  • Anonymous
    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 working

  • Anonymous
    May 24, 2007
    You've been kicked (a good thing) - Trackback from DotNetKicks.com

  • Anonymous
    May 24, 2007
    The comment has been removed

  • Anonymous
    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#. LINQ

  • Anonymous
    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 types

  • Anonymous
    July 10, 2008
    Lists represent the backbone of functional programming and in order to be an effective F# programmer

  • Anonymous
    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.html

  • Anonymous
    December 29, 2008
    OMG! I got pulled off my usual job of doing a bunch of things and have been focused on getting students

  • Anonymous
    May 26, 2009
    第一篇,从零开始编写我们的第一个F#程序。 什么是F#,我为何要学它? F#是一种.NET平台上的 函数式编程 语言。就像C#和VB.NET,F#可以利用.NET的核心类库,如 WPF , WCF ,