.NET 3.5: Properties and Delegates: A New Solution to an Old Problem

4:33 pm programming

A while ago I ranted about how .NET properties didn’t play nice with delegates. The gist was that, if you had a property and a method which took a delegate of the appropriate type, you had to write your own new delegate method, even though the property’s getter and setter would ordinarily qualify. Here’s an example:

public class Foo {
  public String Bar { get; set; }
}

public delegate String GetString();
public void display(GetString getString){
  Console.WriteLine(getString());
}

public void performOnAValue(Action <String> action){
  action("A Value");
}

Foo foo = new Foo { Bar = "bar" };
// You can't do this, because .NET doesn't recognize
// Foo.Bar.get() and Foo.Bar.set() as valid methods
// performOnAValue(foo.Bar);    // foo.Bar.set() satisfies Action<String>
// display(Foo.Bar);            // foo.Bar.get() satisfies GetString

// Instead you have to do this in .NET 2.0:
performOnAValue(delegate(String value) {
  foo.Bar = value;
});
display(delegate() {
  return foo.Bar;
});

Unfortunately, .NET 3.5 doesn’t fix this. However, it does give us enough new features that we can fake it. Consider this:

public static class Getter {
  public static T get<T>(this T target) {
    return target;
  }
}

This extension method adds a get() method to every type. That means that we can do this:

display(foo.Bar.get<String>);

Even though Getter.get() doesn’t satisfy the GetString() signature (since it takes a <T>), .NET is smart enough to figure out that if you call it as an extension it will work.

The bad news is that you do have to specify the type of the object as the generic type parameter of get(). You could write a non-generic version that gets and returns an Object, but that leads to the potential for InvalidCastExceptions. The good news is that if you do use the wrong generic type parameter (let’s say foo.Bar.get<int>), the compiler will complain (albeit with a confusing error message).

More bad news: there’s no equivalent extension method for the setter. Without resorting to reflection, there’s no way (that I am aware of) to programmatically access an arbitrary property of an object from within a method. However, lambda expressions make the pain of writing a delegate a little easier to bear:

performOnAValue(value => foo.Bar = value);

You can also do the same for the getter, if you use an empty input parameter list and an inferred return keyword:

display( () => foo.Bar );

I’m not sure whether the lambda or the extension method getter is less ugly.

With these options, I’d say that the Stupid .NET Trick of properties and delegates is about 75% solved. The syntax is not as clean as it could be, but it’s a big step forward… possibly enough to make properties worth it again.

3 Responses

  1. Ted Says:

    I think your blog has swallowed some greater-than less-than signs in the latter half of the essay.

  2. Craig Says:

    Thanks… fixed that.

  3. Веб-обзор #3: и снова 6 интересных статей для вечернего прочтения. | Alpha-Beta-Release B Says:

    [...] методы, расширение методов и свойств объектов, свойства и делегаты. Без сомнения, очень полезно для .NET-разработчиков, [...]

Leave a Comment

Your comment

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.