扩展System.Object时如何避免装箱/拆箱?

时间:2010-04-09 16:17:01

标签: c# .net extension-methods boxing unboxing

我正在开发一种仅适用于引用类型的扩展方法。但是,我认为它目前正在装箱并取消装箱。我怎么能避免这个?

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);
        }
    }
}

使用示例:

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

在某些方法中:

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

2 个答案:

答案 0 :(得分:4)

那不是拳击。你觉得拳击在哪里?如果是因为你看过“==”附近的IL,不要让它欺骗你 - JIT决定在这里做什么。它有机会为每个(TTResult)对生成不同的本机代码。实际上,代码将为所有引用类型共享,并且值类型不同。所以你最终得到:

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)

话虽如此,如果您想将扩展方法限制为引用类型,请执行以下操作:

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

IL中仍然会有一个盒子,但不要担心 - 实际上不会发生拳击。毕竟,可以被装箱?您正在提供引用,并且引用本身永远不会装箱 - 只有值类型值被装箱。

答案 1 :(得分:2)

简单地说,该代码中没有任何内容需要装箱。 场景,其中装箱是不可避免的,并且在某些情况下还存在用于弥合值/ ref类型(constrained)之间差距的其他操作码。

但不是在这种情况下;不需要实际的拳击(JIT可以删除几个类似盒子的情况 - 但不是全部,可悲的是)