views:

115

answers:

2

I'm working on an extension method that's only applicable to reference types. I think, however, it's currently boxing and unboxing the the value. How can I avoid this?

namespace System
{
    public static class SystemExtensions
    {
        public static TResult GetOrDefaultIfNull<T, TResult>(this T obj, Func<T, TResult> getValue, TResult defaultValue)
        {
            if (obj == null)
                return defaultValue;
            return getValue(obj);
        }
    }
}

Example usage:

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

In some method:

Foo aFooObject = new Foo { Bar = 1 };
Foo nullReference = null;

Console.WriteLine(aFooObject.GetOrDefaultIfNull((o) => o.Bar, 0));  // results: 1
Console.WriteLine(nullReference.GetOrDefaultIfNull((o) => o.Bar, 0));  // results: 0
+4  A: 

That's not boxing. Where do you think it is boxing? If it's because you've looked at the IL around "==", don't let it fool you - the JIT gets to decide what to do here. It has the change to generate different native code for each (T, TResult) pair. In fact, the code will be shared for all reference types, and differ for value types. So you'd end up with:

T = string, TResult = int (native code #1)
T = Stream, TResult = byte (native code #2)
T = string, TResult = byte (native code #2)
T = Stream, TResult = string (native code #3)

Having said that, if you want to restrict your extension method to reference types, do so:

public static TResult GetOrDefaultIfNull<T, TResult>
    (this T obj, Func<T, TResult> getValue, TResult defaultValue)
    where T : class

There'll still be a box in the IL, but don't worry - no boxing will actually occur. After all, what could be boxed? You're providing a reference, and references themselves are never boxed - only value type values are boxed.

Jon Skeet
Interesting that's it's not boxing. This code compiled: int i = 1; i.GetOrDefaultIfNull((o) => o.ToString(), "");Thanks for the "where T : class" as that's really what I was looking for.
Robert H.
+2  A: 

Simply, there is nothing in that code that would require boxing. There are scenarios where boxing is unavoidable, and there are also additional opcodes for bridging the gap between value/ref types (constrained) in some cases.

But not in this case; no actual boxing required (the JIT can remove a few box-like cases - but not all, sadly)

Marc Gravell