对值类型调用方法是否会导致 .NET 中装箱?

发布于 2024-07-10 20:58:33 字数 1001 浏览 14 评论 0原文

我刚刚参与了 Stack Overflow 问题.NET 中的所有内容都是对象吗?< /a>

一位发帖者(在已接受答案的评论中)似乎认为对值类型执行方法调用会导致装箱。 他向我指出装箱和拆箱(C# 编程指南),它没有准确指定我们正在描述的用例。

我不是一个相信单一来源的人,所以我只是想获得有关该问题的进一步反馈。 我的直觉是没有拳击,但我的直觉确实很糟糕。 :D

进一步详细说明:

我使用的示例是:

int x = 5;
string s = x.ToString(); // Boxing??

如果所讨论的结构覆盖从对象继承的方法(如此处接受的答案所述),则装箱不会发生。

但是,如果结构体不重写该方法,则会先执行“约束”CIL 命令到 callvirt。 根据文档,操作码。约束场,这会导致装箱

如果 thisType 是值类型并且 thisType 没有实现方法 然后 ptr 被取消引用、装箱,并且 作为“this”指针传递给 callvirt方法指令。

I was just participating in Stack Overflow question Is everything in .NET an object?.

And one poster (in comments of accepted answer) seemed to think that performing a method call on a value type resulted in boxing. He pointed me to Boxing and Unboxing (C# Programming Guide) which doesn't exactly specify the use case we're describing.

I'm not one to trust a single source, so I just wanted to get further feedback on the question. My intuition is that there is no boxing but my intuition does suck. :D

To further elaborate:

The example I used was:

int x = 5;
string s = x.ToString(); // Boxing??

Boxing does not occur if the struct in question overrides the method inherited from the object as the accepted answer here states.

However if the struct doesn't override the method, a "constrain" CIL command is executed prior to a callvirt. According to the documentation, OpCodes.Constrained Field, this results in boxing:

If thisType is a value type and
thisType does not implement method
then ptr is dereferenced, boxed, and
passed as the 'this' pointer to the
callvirt method instruction.

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

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

发布评论

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

评论(4

深爱成瘾 2024-07-17 20:58:33

这是您的代码的 IL:

L_0001: ldc.i4.5      // get a 5 on the stack
L_0002: stloc.0       // store into x
L_0003: ldloca.s x    // get the address of x on the stack
L_0005: call instance string [mscorlib]System.Int32::ToString()  // ToString
L_000a: stloc.1       // store in s

所以本例中的答案是否定的。

Here's the IL for your code:

L_0001: ldc.i4.5      // get a 5 on the stack
L_0002: stloc.0       // store into x
L_0003: ldloca.s x    // get the address of x on the stack
L_0005: call instance string [mscorlib]System.Int32::ToString()  // ToString
L_000a: stloc.1       // store in s

So the answer in this case is no.

短暂陪伴 2024-07-17 20:58:33

在您给出的情况下,答案是否定的,正如底座指出的那样。

但是,如果您通过接口指针调用方法,则会出现这种情况。

考虑代码:

interface IZot
{
    int F();
}

struct Zot : IZot
{
    public int F()
    {
        return 123;
    }
}

那么

Zot z = new Zot();
z.F();

不会导致装箱:

.locals init (
    [0] valuetype ConsoleApplication1.Zot z)
L_0000: nop 
L_0001: ldloca.s z
L_0003: initobj ConsoleApplication1.Zot
L_0009: ldloca.s z
L_000b: call instance int32 ConsoleApplication1.Zot::F()
L_0010: pop 
L_0011: ret 

但是,这会:

IZot z = new Zot();
z.F();

   .locals init (
        [0] class ConsoleApplication1.IZot z,
        [1] valuetype ConsoleApplication1.Zot CS$0$0000)
    L_0000: nop 
    L_0001: ldloca.s CS$0$0000
    L_0003: initobj ConsoleApplication1.Zot
    L_0009: ldloc.1 
    L_000a: box ConsoleApplication1.Zot
    L_000f: stloc.0 
    L_0010: ldloc.0 
    L_0011: callvirt instance int32 ConsoleApplication1.IZot::F()
    L_0016: pop 

In the case you have given the answer is no, as plinth pointed out.

However, it will if you call a method through an interface pointer.

Consider the code:

interface IZot
{
    int F();
}

struct Zot : IZot
{
    public int F()
    {
        return 123;
    }
}

Then

Zot z = new Zot();
z.F();

Does not result in boxing:

.locals init (
    [0] valuetype ConsoleApplication1.Zot z)
L_0000: nop 
L_0001: ldloca.s z
L_0003: initobj ConsoleApplication1.Zot
L_0009: ldloca.s z
L_000b: call instance int32 ConsoleApplication1.Zot::F()
L_0010: pop 
L_0011: ret 

However, this does:

IZot z = new Zot();
z.F();

   .locals init (
        [0] class ConsoleApplication1.IZot z,
        [1] valuetype ConsoleApplication1.Zot CS$0$0000)
    L_0000: nop 
    L_0001: ldloca.s CS$0$0000
    L_0003: initobj ConsoleApplication1.Zot
    L_0009: ldloc.1 
    L_000a: box ConsoleApplication1.Zot
    L_000f: stloc.0 
    L_0010: ldloc.0 
    L_0011: callvirt instance int32 ConsoleApplication1.IZot::F()
    L_0016: pop 
酒解孤独 2024-07-17 20:58:33

@ggf31316

“我相信调用 ToString,
等于和 Gethashcode 结果为
如果结构没有,则进行拳击
重写方法。”

我已经为你检查了 ToString。Int32 确实覆盖了 ToString,所以我创建了一个不覆盖 ToString 的结构。我使用了 .NET Reflector 以确保该结构不会以某种方式神奇地覆盖 ToString(),但事实并非如此,

因此代码如下:

using System;

namespace ConsoleApplication29
{
    class Program
    {
        static void Main(string[] args)
        {
            MyStruct ms = new MyStruct(5);
            string s = ms.ToString();
            Console.WriteLine(s);
        }
    }

    struct MyStruct
    {
        private int m_SomeInt;

        public MyStruct(int someInt)
        {
            m_SomeInt = someInt;
        }

        public int SomeInt
        {
            get
            {
                return m_SomeInt;
            }
        }
    }
}

MSIL(通过 ILDASM) 是这样的:

  IL_0000:  ldloca.s   ms
  IL_0002:  ldc.i4.5
  IL_0003:  call       instance void ConsoleApplication29.MyStruct::.ctor(int32)
  IL_0008:  ldloca.s   ms
  IL_000a:  constrained. ConsoleApplication29.MyStruct
  IL_0010:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_0015:  stloc.1
  IL_0016:  ldloc.1
  IL_0017:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001c:  ret

现在,尽管没有发生拳击调用,如果您检查 < a href="http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.constrained.aspx" rel="nofollow noreferrer">有关 constrained + 调用 virt 的文档,你会发现它表明拳击确实发生了oOo

引用:

如果 thisType 是值类型并且
thisType 没有实现方法
然后 ptr 被取消引用、装箱,并且
作为“this”指针传递给
callvirt方法指令。

@ggf31316

"I believe that calling ToString,
Equals and Gethashcode result in
boxing if the structure does not
override the methods."

I have checked ToString for you. Int32 does override ToString, so I made a struct that doesn't. I used .NET Reflector to ensure that the struct wasn't somehow magically overriding ToString(), and it wasn't.

So the code was like this:

using System;

namespace ConsoleApplication29
{
    class Program
    {
        static void Main(string[] args)
        {
            MyStruct ms = new MyStruct(5);
            string s = ms.ToString();
            Console.WriteLine(s);
        }
    }

    struct MyStruct
    {
        private int m_SomeInt;

        public MyStruct(int someInt)
        {
            m_SomeInt = someInt;
        }

        public int SomeInt
        {
            get
            {
                return m_SomeInt;
            }
        }
    }
}

And the MSIL (via ILDASM) for the Main method is this:

  IL_0000:  ldloca.s   ms
  IL_0002:  ldc.i4.5
  IL_0003:  call       instance void ConsoleApplication29.MyStruct::.ctor(int32)
  IL_0008:  ldloca.s   ms
  IL_000a:  constrained. ConsoleApplication29.MyStruct
  IL_0010:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_0015:  stloc.1
  IL_0016:  ldloc.1
  IL_0017:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001c:  ret

Now, despite no boxing call taking place, if you check the documentation about a constrained + a call virt, you will find it states that boxing DOES take place. oOo

Quote:

If thisType is a value type and
thisType does not implement method
then ptr is dereferenced, boxed, and
passed as the 'this' pointer to the
callvirt method instruction.

欲拥i 2024-07-17 20:58:33

我相信,如果结构不重写方法,调用 ToString、Equals 和 Gethashcode 会导致装箱。

I believe that calling ToString, Equals and Gethashcode result in boxing if the structure does not override the methods.

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