1. 值类型都是从 System.ValueType继承的,并且都是Sealed。无法再次被继承。

在Reflector中查看ValueType原型如下,重写了Equals, ToString,GetHashCode.

因而在调用这些方法的时候,无需进行装箱操作:

[Serializable, ComVisible(true)]
public abstract class ValueType
{
    // Methods
    protected ValueType();
    [MethodImpl(MethodImplOptions.InternalCall)]
    private static extern bool CanCompareBits(object obj);
    public override bool Equals(object obj);
    [MethodImpl(MethodImplOptions.InternalCall)]
    private static extern bool FastEqualsCheck(object a, object b);
    [MethodImpl(MethodImplOptions.InternalCall)]
    public override extern int GetHashCode();
    public override string ToString();
}
public override string ToString()
{
    return base.GetType().ToString();
}
以System.Byte为例:
public override string ToString()
{
    return Number.FormatInt32(this, null, NumberFormatInfo.CurrentInfo);
}
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern string FormatInt32(int value, string format, NumberFormatInfo info);
可以看到Byte的ToString方法参数中已经使用的是值类型参数(this)。
而对于GetType或MemberwiseClone。是从Object集成的非虚方法。这些方法期望this参数是指向堆上对象的一个指针。
因此在调用时需要进行装箱操作
[Serializable, ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)]
public class Object
{
    // Methods
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
    public Object();
    public virtual bool Equals(object obj);
    public static bool Equals(object objA, object objB);
    private void FieldGetter(string typeName, string fieldName, ref object val);
    private void FieldSetter(string typeName, string fieldName, object val);
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    protected override void Finalize();
    private FieldInfo GetFieldInfo(string typeName, string fieldName);
    public virtual int GetHashCode();
    [MethodImpl(MethodImplOptions.InternalCall)]
    public extern Type GetType();
    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern bool InternalEquals(object objA, object objB);
    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern int InternalGetHashCode(object obj);
    [MethodImpl(MethodImplOptions.InternalCall)]
    protected extern object MemberwiseClone();
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    public static bool ReferenceEquals(object objA, object objB);
    public virtual string ToString();
}
 
2. 值类型(比如结构体)中定义的成员不应修改类型的任何实例字段。
下面的代码演示了在值类型中提供更改字段的方法后,在调用中容易出现问题
    class Program
    {
        internal interface IChangeBoxedPoint
        {
            void Change(Int32 _x, Int32 _y);
        }

struct Point : IChangeBoxedPoint
        {
            private Int32 X, Y;

public Point(Int32 _x, Int32 _y)
            {
                this.X= _x;
                this.Y = _y;
            }

public void Change(Int32 _x, Int32 _y)
            {
                this.X = _x;
                this.Y = _y;
            }

public override string ToString()
            {
                return string.Format("({0},{1})",X,Y);
            }
        }

static void Main(string[] args)
        {
            Point p = new Point(1, 1);

Console.WriteLine(p);

p.Change(2, 2);
            Console.WriteLine(p);

//装箱
            Object o = p;
            Console.WriteLine(o);

//改变的是拆箱后 位于堆栈上的 值类型实例,而o存储的装箱后(堆上)的地址
            ((Point)o).Change(3, 3);
            Console.WriteLine(o);

//同上
            ((IChangeBoxedPoint)p).Change(4, 4);
            Console.WriteLine(p);

//Object 和 interface都是引用类型,此处没有拆装箱操作
            ((IChangeBoxedPoint)o).Change(5, 5);
            Console.WriteLine(o);

Console.ReadKey();
        }
    }

执行结果为:
(1,1)
(2,2)
(2,2)
(2,2)
(2,2)
(5,5)
上述第3个和第4个并没有输出(3,3)(4,4)
这是因为改变的是拆箱之后的堆栈中的值类型,而原装箱的引用不会变化。
不仔细分析很难看出正确的结果,当然没必要时刻当心这个问题,只需将struct 改成class后,一切都清晰多了。
 3. 参数传递
c#中函数参数传递包括传值和传址类型。对于值类型,传值将复制一份值类型的Copy并传递给函数参数,而传值将把值类型在堆栈上的存储地址传递给函数参数;
而对于引用类型,传值将复制引用类型在堆栈上的引用(指针),该copy和原引用类型指向同一对象,因此也可以修改对象内容,
而传址将传递该引用类型在堆栈上指针的Copy,因此可以修改对象本身包括新创对象。
Q:传址方式的两种out 和ref 有什么区别和联系?
A:1. 从CLR和IL的角度看,2者是一致的:都生成对被传递内容的指针。
   2. 关键区别在于编译器保证代码的正确性,在对引用类型的传递时,out 传递需要保证函数过程中实例化,而ref 将检查传入引用是否已经实例化。
   3. 只存在与out 和 ref 差异的重载是不合法的。
Q: 引用类型按值传递也能在函数中改变对象内容,字符串是引用类型,为什么表现得和值类型差不多?
A: 先看下列代码:
Code
class A
{
  public int i = 2;
  override String ToString()
  {
    return i.ToString();
  }
}

f (A a)
{
  a.i = 1;
}

f(Int32 i)
{
  i = 1;
}

F(String s)
{
  s = "1";
}

Main()
{
  A a1 = new A();
  Int 32 i1 = 2;
  String s1 = "2";

Console.WriteLine(a1);  //Display 2
  Console.WriteLin(i1);  //Display 2
  Console.WriteLin(s1);  //Display 2

f(a1);
  f(i1);
  f(s1);

Console.WriteLine(a1);  //Display 1
  Console.WriteLin(i1);  //Display 2
  Console.WriteLin(s1);  //Display 2
}

从结果看,String类型和Int32类型表现得差不多。
实际上这是因为字符串对象的不可改变性造成的,即我们不能改变String类型在堆中的内容,每次赋值其实是在堆中重新创建一个String对象,并将新地址赋给原引用。
按值传递字符串时,原引用s1和形参s指向堆中同一字符串对象,对s赋值时,堆中将新建内容为1的字符串对象,并且s将指向它。但这并不会对原引用s1造成任何影响。
4. params关键字实现可变参数传递
void f( params Int32[] values){
  foreach (Int32 i in values) ...
}
调用:f();f(1);f(1,2);f(3,4,2,23,3)
由于数值对象在堆中分配,最终需要垃圾收集器回收,使用params会导致一些额外的开销,最好多定义几个常用的重载。如:
f(Int32 i)
f(Int32 i1,Int32 i2)

转载于:https://www.cnblogs.com/calmzeal/archive/2008/10/28/1321478.html

【CLR Via C#笔记】 值类型与拆装箱、参数传递相关推荐

  1. 《CLR via C#》读书笔记 之 基元类型、引用类型和值类型

    第五章 基元类型.引用类型和值类型 2013-02-27 5.3 值类型的装箱和拆箱     5.3.2 对象的相等性和同一性 参考 ToDo: 什么时候使用值类型,什么时候使用引用类型 5.3 值类 ...

  2. .NET六大剑客:栈、堆、值类型、引用类型、装箱和拆箱

    .NET六大剑客:栈.堆.值类型.引用类型.装箱和拆箱 一."堆","栈"专区 这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道..."堆&quo ...

  3. 引用类型和值类型学习笔记

    一.基本概念 CLR支持两种类型,值类型和引用类型.它们从类型的定义.实例的创建.参数传递.到内存的分配都有所不同:.NET中的类型分类如下: 值类型和引用类型最本质的区别在于内存的分布上,大致可以这 ...

  4. .net框架读书笔记---基础类型

    接上一篇.net框架读书笔记---值类型的装箱与拆箱, 一.Object CLR要求每个类型都最终集成自System.Object类型,这意味着以下两种定义是相同的: //隐式继承自Object cl ...

  5. 第五章 基元类型,引用类型和值类型

    目录 5.1 编程语言的基元类型 5.2 引用类型和值类型 5.3 值类型的装箱和拆箱 5.4 对象哈希码 5.5 dynamic基元类型 5.1 编程语言的基元类型 编译器直接支持的数据类型称为基元 ...

  6. 述说C#中的值类型和引用类型的千丝万缕

    关于值类型和引用类型方面的博客和文章可以说是汗牛充栋了,今天无意中又复读了一下这方面的知识,感觉还是有许多新感悟的,就此时间分享一下: CLR支持两种类型:值类型和引用类型,看起来FCL的大多数类型是 ...

  7. c#值类型和引用类型

    值类型:整型.布尔型.字符型.实数型.结构型.枚举型. 引用类型:类.对象.字符串.数组.接口.委托. 区别: 1.值类型通常被分配在栈上,它的变量直接包含变量的实例,使用效率比较高. 2.引用类型分 ...

  8. [你必须知道的.NET]第九回:品味类型---值类型与引用类型(中)-规则无边

    发布日期:2007.5.28 作者:Anytao ©2007 Anytao.com ,原创作品,转贴请注明作者和出处. 接上回[第八回:品味类型---值类型与引用类型(上)-内存有理]的探讨,继续我们 ...

  9. 值类型和引用类型的区别,应该很全的。

    区别: 1.值类型通常被分配在栈上,它的变量直接包含变量的实例,使用效率比较高. 2.引用类型分配在托管堆上,引用类型的变量通常包含一个指向实例的指针,变量通过该指针来引用实例. 3.值类型继承自Va ...

  10. 关于.net中值类型的方法调用

    最近在看关于box和unbox的内存分配问题,发现一旦值类型调用了基类的方法或接口的时候就会发生装箱操作.因为基类型的方法或接口必须通过TypeHandle获得.由此引出了一个问题.若所执行的方法并不 ...

最新文章

  1. OpenCV持久化(二)
  2. Science重磅:DeepMind再获突破,用AI开启理解电子相互作用之路
  3. NIO中的SelectionKey
  4. 掉一根头发,搞定二叉排序(搜索)树
  5. 前端学习(1370):错误处理中间件
  6. IDEA 创建 SpringBoot 项目
  7. CSS 奇技淫巧:动态高度过渡动画
  8. Android开发笔记(一百六十八)为应用绑定通知渠道并展示消息角标
  9. ASP.NET 首页性能的4大做法
  10. draw9patch做一个中心不变形的图片
  11. tensorrt 分割_超多,超快,超强!百度飞桨发布工业级图像分割利器PaddleSeg
  12. java web服务器cpu占用过高的处理 (2014-07-21 17:17:36)
  13. eclipse换炫酷主题
  14. Linux系统CPU占用100%原因分析
  15. Linux系统日志分析与管理
  16. maxlength中文和英文html,让input maxlength区分中英文
  17. python可以这样学豆瓣_用python爬取豆瓣短评,这是我见过最牛逼的教程!
  18. 基于ONNX人脸识别实例(SCRFD/ArcFace)-C#版
  19. Win10Chrome调试安卓Chrome
  20. linux查看群组所属用户,linux 列出用户所属的所有群组的5种方法

热门文章

  1. 什么是Tensor Flow和lite以及数据流图
  2. Const限定符与C++11Constexpr的区别
  3. 神经损伤怎么康复好 成都顾连康复医院专科专治
  4. Spring Cloud Spring Boot mybatis 企业分布式微服务云(五)服务消费(Feign)【Dalston版】...
  5. VSCode好用的Python插件及配置
  6. 《Windows 8 权威指南》——2.10 几招解决Windows 8 Metro应用打不开的问题
  7. MongoDB学习笔记~官方驱动的原生Curd操作
  8. 「我们的首要之务,并不是遥望模糊的远方,而是专心处理眼前的事务。」---这是卡内基先生所强调的克服忧虑、开创人生的关键。...
  9. SIP Trunk / SIP 中继服务
  10. Python热门开源项目TOP10