C# Essential 6.0 Note(7)

值类型。

值类型与引用类型:

值类型:

值类型的变量直接包含值,变量引用的位置就是值在内存中实际存储的位置。因此,将一个原始变量的值赋给另一个变量,会在新变量的位置创建原始变量的值的一个内存副本。两个变量不可能引用同一个内存位置。同样的,在方法内部对参数值进行任何修改都不会影响调用者中的原始值。由于值类型需要有一个内存副本,所以定义时通常不要让它消耗太多内存。

值类型的值一般只是短时间存在,很多情况下,这样的值只是作为表达式的一部分,或用于激活方法,在这些情况下,值类型的变量和临时值经常是存储在成为栈的临时存储池中。

临时池清理的代价低于需要垃圾回收的堆,不过,值类型要比引用类型更频繁的复制,这种复制操作会增加性能的开销。

引用类型:

引用类型的值是对一个对象实例的引用,通常是内存地址,要去那个位置找到对象实例的数据,为了访问数据,运行时要从变量中读取引用,然后对它进行解引用,从而到达包含实例数据的内存地址。对于引用类型的变量,它的值要么是null,要么是对需要进行垃圾回收的堆上的一个存储位置的引用。

结构:

定义自定义值类型使用的是和定义类和接口相似的语法,区别在于值类型使用关键字struct。

结构的初始化:

除了属性和字段,结构还可包含方法和构造器。结构不允许包含用户定义的默认构造器。在没有提供默认的构造器时,C#编译器自动的产生一个默认的构造器将所有字段初始化为各自的默认值。

为了确保值类型的局部变量能被完全初始化,结构的每个构造器都必须初始化结构中的所有字段。如果对结构的所有数据初始化失败,会造成编译时错误。当实例化一个包含了未赋值的值类型字段的引用类型,或者实例化一个没有初始化器的值类型数组时,应该显式的初始化。

在初始化好所有的字段之前,访问this是非法的。

装箱:

将值类型转换成它实现的某个接口或者object,转换的结果必然是对一个存储位置的引用,该变量表面上包含引用类型的实例,但实际上包含值类型的值。这种转换称为装箱转换。

  1. 在堆上分配内存:它将用于存储值类型的数据以及少许额外开销。
  2. 接着发生一次内存复制,当前存储位置的值类型数据被复制到堆上分配好的位置。
  3. 转换结果是对堆上新存储位置的引用。

相反的过程称之为拆箱,先检查已装箱的值的类型兼容于要拆箱的值的类型,然后复制堆中存储的值。

    public class Program
    {
        static void Main()
        {
            int totalCount;
            System.Collections.ArrayList list = new System.Collections.ArrayList();

            Console.Write("Enter a number between 2 and 1000:");
            totalCount = int.Parse(Console.ReadLine());

            list.Add((double)0);    // box
            list.Add((double)1);    // box
            for (int count = 2; count < totalCount; count++)
            {
                list.Add((double)list[count - 1] + (double)list[count - 2]);
                // two unbox and one box per command
            }

            foreach(double count in list)
            {
                Console.Write("{0}, ", count);
                // box per command
            }
        }
    }

每次装箱操作都涉及内存分配和复制,每个拆箱操作都涉及类型检查和复制。如果用已拆箱的类型做同样的事情,就可以避免内存分配与类型检查。

拆箱时的类型

要在运行时成功拆箱值类型,被拆箱的项必须是对一个对象的引用,该对象是先前通过装箱该值类型的实例创建的。尝拆箱 null 会导致 NullReferenceException。 尝试拆箱对不兼容值类型的引用会导致InvalidCastException

int number;
object thing;
double bigNumber;

number = 42;
thing = number;
// bigNumber = (double)thing; InvalidCastException 这里的thing先前通过装箱int类型的实例创建,与double不兼容
bigNumber = (double)(int)thing;
避免可变的值类型:
using System;

namespace learnCsharp
{
    public class Program
    {
        static void Main()
        {
            Angle angle = new Angle(25, 58, 23);
            object objectAngle = angle; // box
            Console.Write(((Angle)objectAngle).Degrees);

            //1.unbox,modify unbox value, and discard value;
            ((Angle)objectAngle).MoveTo(26, 58, 23);
            Console.Write(", " + ((Angle)objectAngle).Degrees);

            //2.box,modify boxed value, and discard reference to box
            ((IAngle)angle).MoveTo(26, 58, 23);
            Console.Write(", " + ((Angle)angle).Degrees);

            //3.Modify boxed value directly
            ((IAngle)objectAngle).MoveTo(26, 58, 23);
            Console.WriteLine(", " + ((Angle)objectAngle).Degrees);

        }
    }

    interface IAngle
    {
        void MoveTo(int degrees, int minutes, int seconds);
    }

    struct Angle : IAngle
    {
        public Angle(int degrees,int minutes,int seconds)
        {
            Degrees = degrees;
            Minutes = minutes;
            Seconds = seconds;
        }

        public void MoveTo(int degrees, int minutes, int seconds)
        {
            Degrees = degrees;
            Minutes = minutes;
            Seconds = seconds;
        }

        public int Degrees { get; set; }

        public int Minutes { get; set; }

        public int Seconds { get; set; }
    }
}

值类型的变量就像是上面写了值的纸,对值进行装箱,相当于对纸进行复印,并把复印件装到箱子里。

对值进行拆箱,相当于对箱子里的纸进行复印。编辑第二份复印件不会对箱子里的副本造成影响。

  1. 为了调用MoveTo(),编译器对objectAngle进行拆箱,并且创建值的副本。值类型根据值进行复制,虽然结果值在执行时被成功修改,但是值的这个副本会被丢弃。objectAngle引用的堆位置没有发生任何改变。
  2. 第二次,将值类型转换成IAngle。将值类型转换为接口类型会进行装箱。运行时将angle的值复制到堆上,并提供对箱子的引用,接着,方法调用会修改被引用的箱子中的值,存储在angle变量中的值保持未修改状态。
  3. 第三次,向IAngle的转型为引用转换(在实现类与接口之间转换)而非装箱转换,值已经通过向objectAngle的转换装箱了,这次不会发生值的复制,对MoveTo的调用将更新箱子中的对应值,代码的行为符合预期。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ssential C# 7.0 is a well-organized, no-fluff guide to C# 7.0 for programmers at all levels of experience. Reflecting the most important C# features from 3.0 through 7.0 and including modern programming patterns, it will help you write code that’s simple, powerful, robust, secure, and maintainable. Author Mark Michaelis is a world-class C# expert: a long-time Microsoft MVP and Regional Director who also has served on Microsoft’s C# design review team. He presents a comprehensive tutorial and reference for the entire language, including expert coverage of key C# 7.0 enhancements, C# 7.0’s use with .NET Core/.NET Standard, and cross-platform compilation. He illustrates key C# constructs with succinct examples, and presents best-practice coding guidelines. To help you maintain existing code, separate indexes provide version-specific answers for C# 5.0, 6.0, and 7.0, and visual icons show when each language innovation was introduced. Make the most of C# 7.0 enhancements, including tuples, deconstructors, pattern matching, local functions, and ref returns Work efficiently with C# data types, operators, control flow, methods, and parameters Write more robust code with C# object-oriented constructs Implement reliable, effective exception handling Reduce code complexity with generics, delegates, lambda expressions, and events Leverage advanced dynamic and declarative programming techniques Query diverse data collections using LINQ with query expressions Create custom collections that operate against business objects Access .NET collections via collection interfaces and standard query operators Master multithreading and synchronization, including the async/await paradigm Optimize performance and interoperability with P/Invoke and unsafe code Run your code on Linux or macOS with C# 7.0 cross-platform compilation Includes C# 7.1, 7.2, and 7.3 language enhancements This guide offers you a complete foundation for successful development with modern versions of the C# language in any project or environment. Table of Contents Chapter 1 Introducing C# Chapter 2 Data Types Chapter 3 More with Data Types Chapter 4 Operators and Control Flow Chapter 5 Methods and Parameters Chapter 6 Classes Chapter 7 Inheritance Chapter 8 Interfaces Chapter 9 Value Types Chapter 10 Well-Formed Types Chapter 11 Exception Handling Chapter 12 Generics Chapter 13 Delegates and Lambda Expressions Chapter 14 Events

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值