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

发布于 2024-08-28 11:12:45 字数 788 浏览 16 评论 0原文

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

︶葆Ⅱㄣ 2024-09-04 11:12:45

那不是拳击。你认为这是拳击的什么地方?如果这是因为您已经查看了“==”周围的 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 中仍然会有一个框,但不用担心 - 实际上不会发生装箱。毕竟,什么可以被装箱呢?您提供了一个引用,并且引用本身永远不会被装箱 - 只有值类型值才会被装箱。

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.

寻找我们的幸福 2024-09-04 11:12:45

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

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

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)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文