• 基础系列(7)—— 结构


    一 结构的定义

           结构用 struct 关键字定义的,与类相似,都表示可以包含数据成员和函数成员的数据结构。数据成员主要包括常量、变量、构造函数、方法、属性等。同时结构的特征为: 结构是一种值类型,并且不需要堆分配。 结构的实例化可以不使用 new 运算符。可以看做是“轻量级的类”

      (1)定义结构的语法:

    <访问修饰符> struct 结构名
    {
    //结构体
    }   

      (2)什么时候使用结构

       结构体适合一些小型数据结构,这些数据结构包含的数据以创建结构后不修改的数据为主例如:struct类型适于表示PointRectangle和Color等轻量对象。尽管可以将一个点表示为类,但在某些情况下,使用结构更有效。如果声明一个10000个Point对象组成的数组,为了引用每个对象,则需分配更多内存;这种情况下,使用结构可以节约资源。定义的时候不会用到面向对象的一些特性;结构体在不发生装箱拆箱的情况下性能比类类型是高很多的.

        在结构声明中,除非字段被声明为 const 或 static,否则无法初始化赋值。 结构类型永远不是抽象的,并且始终是隐式密封的,因此在结构声明中不允许使用abstract和sealed修饰符结构不能声明默认构造函数(没有参数的构造函数)或析构函数,但可以声明带参数的构造函数。 结构可以实现接口,但不能从另一个结构或类继承,而且不能作为一个类的基,所有结构都直接继承自System.ValueType,后者继承自 System.Object。 结构在赋值时进行复制。 将结构赋值给新变量时,将复制所有数据,并且对新副本所做的任何修改不会更改原始副本的数据。 在使用值类型的集合(如 Dictionary<string, myStruct>)时,请务必记住这一点。 结构类型的变量直接包含了该结构的数据,而类类型的变量所包含的只是对相应数据的一个引用(被引用的数据称为“对象”)。但是结构仍可以通过ref和out参数引用方式传递给函数成员。 结构可用作可以为 null 的类型,因而可向其赋 null 值。 

    struct Simple
        {
            //public int x = 0; //结构中不能对字段进行初始化
            public int x;
            public int y;
            //public Simple()   //报错,结构中不能使用无参数的构造函数
            //{ 
            //}
        }
    
        class Csimple
        {
            public int x;
            public int y;
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Csimple cs1 = new Csimple(),cs2=null; //类的实例
                Simple ss1 = new Simple(), ss2=new Simple(); //结构的实例
                cs1.x = ss1.x = 5;
                cs1.y = ss1.y = 10;
                cs2 = cs1;
                ss2 = ss1;
            }
        }

       类和变量的实例在赋值前后的栈区和堆区的比较

    二 结构的装箱与拆箱

          由于结构不是引用类型,当结构类型的值被转换为object 类型或由该结构实现的接口类型时,就会执行一次装箱操作。 反之,当 object 类型的值或接口类型的值被转换回结构类型时,会执行一次拆箱操作。 与对类类型进行的相同操作相比,主要区别在于: 装箱操作会把相关的结构值复制为已被装箱的实例,而拆箱则会从已被装箱的实例中复制出一个结构值。 因此,在装箱或拆箱操作后,对“箱”外的结构进行的更改不会影响已被装箱的结构。关注于装箱与拆箱的详解讲解,请看:http://www.cnblogs.com/wyh19941210/p/5847086.html

    struct Program
    {
      static void Main(string[] args)
      {
       int i = 1;object o = i; //隐式装箱
       i = 123;
       Console.WriteLine("i={0},o={1}",i,o);
       Console.Read();
       }
    }//结果为:i=123,o=1

     三 结构与构造函数

           我们知道结构不能使用默认的构造函数,只能使用带参数的构造函数,当定义带参数的构造函数时,一定要完成结构所有字段的初始化,如果没有完成所有字段的初始化,编译时会发生错误。 结构可以使用静态构造函数吗? 可以,结构的静态构造函数与类的静态构造函数所遵循的规则大体相同。 结构的静态构造函数何时将触发呢? 结构的实例成员被引用,结构的静态成员被引用,结构显示声明的构造函数被调用。 但是创建结构类型的默认值不会触发静态构造函数。

        为什么结构不能自定义无参数的构造函数? 结构类型的构造函数与类的构造函数类似,用来初始化结构的成员变量,但是struct不能包含显式默认构造函数, 因为编译器将自动提供一个构造函数,此构造函数将结构中的每个字段初始化为默认值表中显示的默认值。 然而,只有当结构用new实例化时,才会调用此默认构造函数。对值类型调用默认构造函数不是必需的。

    struct A
    {
        static A()
        {
          Console.WriteLine("I am A.");
        }
        public void Fun()
        {
    
        }
    }
     
    class Program
    {
      static void Main(string[] args)
      {
      A a=new A();
      a.Fun(); //结构的实例成员被引用Console.Read();
      }
    }
    结果为:I am A.

    四  结构与继承

            一个结构声明可以指定实现的接口列表,但是不能指定基类。 由于结构不支持类与结构的继承,所以结构成员的声明可访问性不能是 protected 或 protected internal。 结构中的函数成员不能是 abstract 或 virtual,因而 override 修饰符只适用于重写从 System.ValueType 继承的方法。 为在设计编程语言时将结构设计成无继承性? 其实类的继承是有相当的成本的 ——由于继承性,每个类需要用额外的数据空间来存储“继承图”来表示类的传承历史, 通俗地说来就是我们人类的家族家谱,里面存储着我们的祖宗十八代,只有这样我们才知道我们从哪里来的,而家谱肯定是需要额外的空间来存放的。 大家不要觉得这个存放“继承图”的空间很小,如果我们的程序需要用10000个点(Point)来存放游戏中的人物形体数据的话, 在一个场景中又有N个人,这个内存开销可不是小数目了。所以我们可以通过将点(Point)申明成 Struct而不是class来节约内存空间。

    interface ITest
    {
    void Fun(int x,int y);
    }
    struct A:ITest
    {
       public void Fun(int x,int y) //隐式实现接口里的方法
       {
       Console.WriteLine("x={0},y={1}", x, y);
      }
    }
    
    class Program
    {
       static void Main(string[] args)
       {
         A a; //结构的实例化可以不使用new
         a.Fun(1, 2);
         Console.Read();
       }
    }
    // 结果为:x=1,y=2

     五 结构与类的关系

    相同点:

       二者实际上都是创建对象的模板,都可以包含构造函数、常量、字段、方法、属性、索引器和运算符等成员

    不同点:

    (1)结构是值类型,类是引用类型在内存中结构是存储在栈取的值类型,类是存储在堆区的引用类型

    (2)结构和类的构造函数不同,结构中不能包含无参数的构造函数,结构成员会自动初始化为它们默认值

    (3)结构不能对实例字段成员进行初始化

    (4)结构不能继承,就是一个结构不能从另外一个结构或者类中继承,而且不能作为一个类的继承

  • 相关阅读:
    spring查看生成的cglib代理类源码详解
    java-jdk动态代理生成的代理类源码
    约瑟夫斯问题-java版数组解法和链表解法
    HashMap源码解析(简单易懂)
    windows云服务器发布项目
    java学习
    TTL macro登陆linux服务器
    c#笔记
    C#笔记
    git merge
  • 原文地址:https://www.cnblogs.com/wyh19941210/p/6682124.html
Copyright © 2020-2023  润新知