Search code examples
c#.netextension-methodsboxingunboxing

How do I avoid boxing/unboxing when extending System.Object?


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

Solution

  • 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 chance 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.