C# 中的拳击发生

发布于 2024-12-13 14:49:42 字数 2309 浏览 3 评论 0原文

我正在尝试收集 C# 中发生装箱的所有情况:

  • 将值类型转换为 System.Object 类型:

    <前><代码> 结构 S { } 对象框 = new S();
  • 将值类型转换为 System.ValueType 类型:

    <前><代码> 结构 S { } System.ValueType box = new S();
  • 将枚举类型的值转换为 System.Enum 类型:

     枚举 E { A }
      System.Enum box = EA;
    
  • 将值类型转换为接口引用:

     接口 I { }
      结构体 S : I { }
      I 框 = new S();
    
  • 在 C# 字符串连接中使用值类型:

     char c = F();
      string s1 = "char 值将装箱" + c;
    

    注意:char类型的常量在编译时连接

    注意:自版本 6.0 C# 起,Roslyn 1.0-beta(>=VS2015 预览版)编译器优化连接,涉及 boolcharIntPtrUIntPtr 类型

    注意:Roslyn 3.3 编译器 (> ;=VS2019.3) 值类型首先转换为字符串,然后连接,因此没有装箱(同样适用于字符串插值在.Net6中)

  • 从值类型实例方法创建委托:

     struct S { public void M() {} }
      动作框 = new S().M;
    
  • 在值类型上调用非重写虚拟方法:

     枚举 E { A }
      EAGetHashCode();
    
  • is 表达式下使用 C# 7.0 常量模式:

     int x = ...;
      if (x is 42) { … } // 将 'x' 和 '42' 都装箱!
    
  • C# 元组类型转换中的装箱:

     (int, byte) _tuple;
    
      公共(对象,对象)M(){
        返回_元组; // 2x 拳击
      }
    
  • 具有值类型默认值的 object 类型的可选参数:

     void M([可选, DefaultParameterValue(42)] object o);
      M(); // 在调用点进行拳击
    
  • 检查无约束泛型类型的值是否为 null

     bool M(T t) =>; t!=空;
      字符串 M(T t) => t?.ToString(); // ?.检查是否为空
      中号(42);
    

    注意:这可能会在某些 .NET 运行时中通过 JIT 进行优化

  • 无约束或 struct 的类型测试值< /code> 带有 is/as 运算符的泛型类型:

     bool M(T t) =>; t 是整数;
      整数? M T (T t)=> t 作为 int?;
      IEquatable M T (T t)=> t为IEquatable;
      中号(42);
    

    注意:这可能会在某些 .NET 运行时中通过 JIT 进行优化

您还知道其他可能是隐藏的装箱情况吗?

I'm trying to collect all of the situations in which boxing occurs in C#:

  • Converting value type to System.Object type:

      struct S { }
      object box = new S();
    
  • Converting value type to System.ValueType type:

      struct S { }
      System.ValueType box = new S();
    
  • Converting value of enumeration type to System.Enum type:

      enum E { A }
      System.Enum box = E.A;
    
  • Converting value type into interface reference:

      interface I { }
      struct S : I { }
      I box = new S();
    
  • Using value types in C# string concatenation:

      char c = F();
      string s1 = "char value will box" + c;
    

    note: constants of char type are concatenated at compile time

    note: since version 6.0 C#, Roslyn 1.0-beta (>=VS2015 preview) compiler optimizes concatenation involving bool, char, IntPtr, UIntPtr types

    note: since Roslyn 3.3 compiler (>=VS2019.3) value types are first converted to string and then concatenated, hence no boxing (same applies for string interpolation in .Net6)

  • Creating delegate from value type instance method:

      struct S { public void M() {} }
      Action box = new S().M;
    
  • Calling non-overridden virtual methods on value types:

      enum E { A }
      E.A.GetHashCode();
    
  • Using C# 7.0 constant patterns under is expression:

      int x = …;
      if (x is 42) { … } // boxes both 'x' and '42'!
    
  • Boxing in C# tuple types conversions:

      (int, byte) _tuple;
    
      public (object, object) M() {
        return _tuple; // 2x boxing
      }
    
  • Optional parameters of object type with value type default values:

      void M([Optional, DefaultParameterValue(42)] object o);
      M(); // boxing at call-site
    
  • Checking value of unconstrained generic type for null:

      bool M<T>(T t) => t != null;
      string M<T>(T t) => t?.ToString(); // ?. checks for null
      M(42);
    

    note: this may be optimized by JIT in some .NET runtimes

  • Type testing value of unconstrained or struct generic type with is/as operators:

      bool M<T>(T t) => t is int;
      int? M<T>(T t) => t as int?;
      IEquatable<T> M<T>(T t) => t as IEquatable<T>;
      M(42);
    

    note: this may be optimized by JIT in some .NET runtimes

Are there any more situations of boxing, maybe hidden, that you know of?

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

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

发布评论

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

评论(5

情场扛把子 2024-12-20 14:49:42

这是一个很好的问题!

发生装箱的原因只有一个:当我们需要对值类型的引用时。您列出的所有内容都属于此规则。

例如,由于对象是引用类型,因此将值类型转换为对象需要对值类型的引用,这会导致装箱。

如果您希望列出每种可能的情况,还应该包括派生类,例如从返回对象或接口类型的方法返回值类型,因为这会自动将值类型转换为对象/接口。

顺便说一句,您敏锐地识别出的字符串连接情况也是源自于对象的转换。 + 运算符由编译器转换为对 string 的 Concat 方法的调用,该方法接受您传递的值类型的对象,因此会强制转换为对象,从而发生装箱。

多年来,我一直建议开发人员记住装箱的单一原因(我在上面指定),而不是记住每一个案例,因为列表很长而且很难记住。这也有助于理解编译器为 C# 代码生成的 IL 代码(例如,字符串上的 + 会产生对 String.Concat 的调用)。当您对编译器生成的内容有疑问并且发生装箱时,您可以使用 IL 反汇编程序 (ILDASM.exe)。通常,您应该查找装箱操作码(只有一种情况可能会发生装箱,即使 IL 不包含装箱操作码,下面有更多详细信息)。

但我确实同意有些拳击事件不太明显。您列出了其中之一:调用值类型的非重写方法。事实上,由于另一个原因,这一点不太明显:当您检查 IL 代码时,您看不到装箱操作码,而是看到约束操作码,因此即使在 IL 中,装箱发生也不明显!我不会详细说明为什么要防止这个答案变得更长...

不太明显装箱的另一种情况是从结构调用基类方法时。示例:

struct MyValType
{
    public override string ToString()
    {
        return base.ToString();
    }
}

此处 ToString 被重写,因此在 MyValType 上调用 ToString 将不会生成装箱。但是,该实现调用基本 ToString 并导致装箱(检查 IL!)。

顺便说一句,这两个不明显的拳击场景也源自上述单一规则。当在值类型的基类上调用方法时,必须有 this 关键字可以引用的内容。由于值类型的基类(始终)是引用类型,因此 this 关键字必须引用引用类型,因此我们需要对值类型的引用,因此由于以下原因会发生装箱:单一规则。

以下是我的在线 .NET 课程中详细讨论拳击部分的直接链接:http://motti.me/mq

如果您只对更高级的拳击场景感兴趣,这里有一个直接链接(尽管上面的链接一旦讨论了更基本的内容也会带您到那里):http://motti.me/mu

我希望这有帮助!

莫蒂

That’s a great question!

Boxing occurs for exactly one reason: when we need a reference to a value type. Everything you listed falls into this rule.

For example since object is a reference type, casting a value type to object requires a reference to a value type, which causes boxing.

If you wish to list every possible scenario, you should also include derivatives, such as returning a value type from a method that returns object or an interface type, because this automatically casts the value type to the object / interface.

By the way, the string concatenation case you astutely identified also derives from casting to object. The + operator is translated by the compiler to a call to the Concat method of string, which accepts an object for the value type you pass, so casting to object and hence boxing occurs.

Over the years I’ve always advised developers to remember the single reason for boxing (I specified above) instead of memorize every single case, because the list is long and hard to remember. This also promotes understanding of what IL code the compiler generates for our C# code (for example + on string yields a call to String.Concat). When your’e in doubt what the compiler generates and if boxing occurs, you can use IL Disassembler (ILDASM.exe). Typically you should look for the box opcode (there is just one case when boxing might occur even though the IL doesn't include the box opcode, more detail below).

But I do agree that some boxing occurrences are less obvious. You listed one of them: calling a non-overridden method of a value type. In fact, this is less obvious for another reason: when you check the IL code you don’t see the box opcode, but the constraint opcode, so even in the IL it’s not obvious that boxing happens! I won't get into the exact detail why to prevent this answer from becoming even longer...

Another case for less obvious boxing is when calling a base class method from a struct. Example:

struct MyValType
{
    public override string ToString()
    {
        return base.ToString();
    }
}

Here ToString is overridden, so calling ToString on MyValType won’t generate boxing. However, the implementation calls the base ToString and that causes boxing (check the IL!).

By the way, these two non-obvious boxing scenarios also derive from the single rule above. When a method is invoked on the base class of a value type, there must be something for the this keyword to refer to. Since the base class of a value type is (always) a reference type, the this keyword must refer to a reference type, and so we need a reference to a value type and so boxing occurs due to the single rule.

Here is a direct link to the section of my online .NET course that discusses boxing in detail: http://motti.me/mq

If you are only interested in more advanced boxing scenarios here is a direct link there (though the link above will take you there as well once it discusses the more basic stuff): http://motti.me/mu

I hope this helps!

Motti

猥︴琐丶欲为 2024-12-20 14:49:42

在值类型上调用非虚拟 GetType() 方法:

struct S { };
S s = new S();
s.GetType();

Calling non-virtual GetType() method on value type:

struct S { };
S s = new S();
s.GetType();
记忆里有你的影子 2024-12-20 14:49:42

Motti的回答中提到,只是用代码示例进行说明:

涉及的参数

public void Bla(object obj)
{

}

Bla(valueType)

public void Bla(IBla i) //where IBla is interface
{

}

Bla(valueType)

但这是安全的:

public void Bla<T>(T obj) where T : IBla
{

}

Bla(valueType)

返回类型

public object Bla()
{
    return 1;
}

public IBla Bla() //IBla is an interface that 1 inherits
{
    return 1;
}

检查不受约束的 T 是否为空

public void Bla<T>(T obj)
{
    if (obj == null) //boxes.
}

使用动态

dynamic x = 42; (boxes)

另一个

enumValue.HasFlag

Mentioned in Motti's answer, just illustrating with code samples:

Parameters involved

public void Bla(object obj)
{

}

Bla(valueType)

public void Bla(IBla i) //where IBla is interface
{

}

Bla(valueType)

But this is safe:

public void Bla<T>(T obj) where T : IBla
{

}

Bla(valueType)

Return type

public object Bla()
{
    return 1;
}

public IBla Bla() //IBla is an interface that 1 inherits
{
    return 1;
}

Checking unconstrained T against null

public void Bla<T>(T obj)
{
    if (obj == null) //boxes.
}

Use of dynamic

dynamic x = 42; (boxes)

Another one

enumValue.HasFlag

疏忽 2024-12-20 14:49:42
  • 在 System.Collections 中使用非泛型集合,例如
    ArrayListHashTable

当然,这些是第一个案例的具体实例,但它们可能是隐藏的陷阱。令人惊讶的是,我今天仍然遇到大量使用这些而不是 ListDictionary 的代码。

  • Using the non-generic collections in System.Collections such as
    ArrayList or HashTable.

Granted these are specific instances of your first case, but they can be hidden gotchas. It's amazing the amount of code I still come across today that use these instead of List<T> and Dictionary<TKey,TValue>.

巷子口的你 2024-12-20 14:49:42

将任何值类型值添加到 ArrayList 中都会导致装箱:

ArrayList items = ...
numbers.Add(1); // boxing to object

Adding any value type value into the ArrayList causes boxing:

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