看着标题是不是很诧异? struct不是值类型么? 怎么是 object?
先看看一段代码, 输出的结果是是什么?
using System;
namespace StructDemo
{
class Program
{
static void Main(string[] args)
{
MyStruct myStruct = new MyStruct();
Console.WriteLine($"my struct is object:{myStruct is object}");
int a = 1;
Console.WriteLine($"int is object:{a is object}");
}
}
struct MyStruct
{
}
}
输出结果如下:
明明我们学习的时候说 struct
是值类型, 为什么代码输出True
?
我们来看看微软的文档怎么来描述的: 常规类型系统 | Microsoft Docs
.NET 中的所有类型不是值类型就是引用类型。
值类型是使用对象实际值来表示对象的数据类型。 如果向一个变量分配值类型的实例,则该变量将被赋以该值的全新副本。
引用类型是使用对对象实际值的引用(类似于指针)来表示对象的数据类型。 如果为某个变量分配一个引用类型,则该变量将引用(或指向)原始值。 不创建任何副本。
.NET 中的通用类型系统支持以下五种类别的类型:
- 类
- 结构
- 枚举
- 接口
- 委托
具体来说:
从上面的文档我们可以看出:
Object 类 (System) | Microsoft Docs 中提到:
支持 .NET 类层次结构中的所有类,并为派生类提供低级别服务。 这是所有 .NET 类的最终基类;它是类型层次结构的根。
其部分源码object.cs如下:
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Class: Object
**
**
** Object is the root class for all CLR objects. This class
** defines only the basics.
**
**
===========================================================*/
namespace System {
using System;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.Versioning;
using System.Diagnostics.Contracts;
using CultureInfo = System.Globalization.CultureInfo;
using FieldInfo = System.Reflection.FieldInfo;
using BindingFlags = System.Reflection.BindingFlags;
#if FEATURE_REMOTING
using RemotingException = System.Runtime.Remoting.RemotingException;
#endif
// The Object is the root class for all object in the CLR System. Object
// is the super class for all other CLR objects and provide a set of methods and low level
// services to subclasses. These services include object synchronization and support for clone
// operations.
//
//This class contains no data and does not need to be serializable
[Serializable]
[ClassInterface(ClassInterfaceType.AutoDual)]
[System.Runtime.InteropServices.ComVisible(true)]
public class Object
{
//此处省略
}
从valuetype.cs看到, 结构的定义代码如下:
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Class: ValueType
**
**
** Purpose: Base class for all value classes.
**
**
===========================================================*/
namespace System {
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
[Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
public abstract class ValueType
{
//此处省略
}
Purpose: Base class for all value classes.
所有的值类型都是 ValueType的子类, 包括: 结构、枚举
enum.cs的源码可看出 Enum : ValueType
:
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System.Reflection;
using System.Text;
using System.Collections;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Diagnostics.Contracts;
namespace System
{
[Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
public abstract class Enum : ValueType, IComparable, IFormattable, IConvertible
{
//此处省略
}
从上述的表述可以总结为下图:
从定义上看 : 值类型都是派生自 ValueType
, ValueType
派生自 Object
, 所以 struct is object
输出为 True.
object 是引用类型, struct 是值类型, 实际怎么判断 struct is object
呢?
这里就涉及到了值类型和引用类型的转换, 即: 装箱和拆箱
object
类型或到此值类型所实现的任何接口类型的隐式转换object
类型到值类型或从接口类型到实现该接口的值类型的显式转换而 struct is object
的过程为 : struct -> ValueType(引用类型) -> Object .
在微软文档装箱和取消装箱 - C# 编程指南 | Microsoft Docs 中我们可以看到:
int 1= 123;
//以下语句对变量 i 隐式应用了装箱操作:
object o = i;
此语句的结果是在堆栈上创建对象引用 o
,而在堆上则引用 int
类型的值。 该值是赋给变量 i
的值类型值的一个副本。 以下装箱转换图说明了 i
和 o
这两个变量之间的差异:
即装箱后的object,包含了值类型的原始类型和值.
取消装箱操作包括:
下面的语句演示装箱和取消装箱两种操作:
int i = 123; // a value type
object o = i; // boxing
int j = (int)o; // unboxing
下图演示了上述语句的结果:
要在运行时成功取消装箱值类型,被取消装箱的项必须是对一个对象的引用,该对象是先前通过装箱该值类型的实例创建的。 尝试取消装箱 null
会导致 NullReferenceException。 尝试取消装箱对不兼容值类型的引用会导致 InvalidCastException。
下面的示例演示无效的取消装箱及引发的 InvalidCastException
。 使用 try
和 catch
,在发生错误时显示错误信息。
class TestUnboxing
{
static void Main()
{
int i = 123;
object o = i; // implicit boxing
try
{
int j = (short)o; // attempt to unbox
System.Console.WriteLine("Unboxing OK.");
}
catch (System.InvalidCastException e)
{
System.Console.WriteLine("{0} Error: Incorrect unboxing.", e.Message);
}
}
}
此程序输出:
Specified cast is not valid. Error: Incorrect unboxing.
struct is object
的过程包含了值类型到引用类型的转换, 也包含了值类型和引用类型的定义划分.
通用类型: