3

我正在研究一种仅适用于引用类型的扩展方法。但是,我认为,它目前正在对值进行装箱和拆箱。我怎样才能避免这种情况?

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
4

2 回答 2

5

那不是拳击。你觉得拳击在哪里?如果是因为您查看了“==”周围的 IL,请不要让它愚弄您 - JIT 将决定在这里做什么。它有机会为每个 ( T, TResult) 对生成不同的本机代码。事实上,所有引用类型的代码都是共享的,而值类型的代码是不同的。所以你最终会得到:

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 中仍然会有一个盒子,但不用担心 - 实际上不会发生装箱。毕竟,什么可以装箱?您正在提供参考,并且参考本身永远不会被装箱 - 只有值类型值被装箱。

于 2010-04-09T16:31:27.893 回答
2

简而言之,该代码中没有任何内容需要装箱。在某些情况下,装箱是不可避免的,在某些情况下,还有一些额外的操作码用于弥合值/引用类型之间的差距 ( constrained)。

但不是在这种情况下;不需要实际的装箱(JIT 可以删除一些类似盒子的箱子 - 但不是全部,遗憾的是)

于 2010-04-09T16:39:20.720 回答