C#结构和类的六点区别

来源:互联网 发布:96年nba总决赛数据 编辑:程序博客网 时间:2024/04/28 12:16

引言

       我们先来看一个例子:

                                                 例1:类和结构的基本定义

 

       上面的两个图片一个定义的是类,另一个是结构的定义。从表面上来看,两中数据类型的定义基本没什么区别,类里面有的成员结构都能有,事实上也确实如此。在c#中,两者在本质上都属于数据结构,封装着一组整体作为一个逻辑单位的数据和行为。 数据和行为是该类或结构的“成员”,它们包含各自的方法、属性和事件等(本主题后面列出了这些内容)。结构和类有很大的相似性:

1、 都是container类型,这表示它们可以包含其他数据类型作为成员。他们可以包含的内容基本相同:字段、构造函数、方法、属性、常量、事件、索引器、运算符、嵌套类型等;

2、 成员都可以分为静态和非静态,成员的类型、访问方式可以互不相同;

3、 方法(或称函数)都可以进行重载、复写等操作;

4、  都派生于System.Object;

5、  结构定义函数和类中定义函数完全相同;

6、 都能进行封装;

7、 都能响应接口;

8、 都可以通过泛型定义;

9、 都可以声明和触发事件,而且两者都可以声明委托(Delegate);

10、             方法或成员的调用方式、对象的初始化都相同;

11、             默认情况下所有的字段、方法都是私有的

结构与类在语法上有着很大的相似,但是两者也存在着很明显的区别,具体表现在下面六个方面:

区别一:存储类型

“栈”(stack)和“堆”(heap)这两个词来源于“运行时”(runtime)对内存进行组织的方式:

     栈内存就像一系列堆叠越高的箱子。调用方法时,它的每个参数都被放入一个箱子,并将这个箱子放到栈的最顶部。每个局部变量也同样分配到一个箱子,并同样放到栈的最顶部。方法结束之后,方法的所有箱子都会从栈中移除。

     堆内存则像散布在房间里的一大堆箱子,而不像栈那样,每个箱子都严格地叠置在另一个箱子上方。每个箱子都有一个标签,它标记了这个箱子是否正在使用。创建一个新对象时,“运行时”会查找一个空箱子,并把它分配给对象。对对象的引用则存储在栈上的一个局部变量中。“运行时”将跟踪每个箱子的引用数量(记住,两个变量可能引用同一个对象)。一旦最后一个引用消失,运行时就将箱子标记为“未使用”。将来某个时候,会清除箱子里的东西,使之能真正重用。

结构和类的存储类型:

       结构是值类型数据,存储在栈(stack)中。结构进行数据复制的时候,是将原来数据进行备份。创建结构时,结构赋值到的变量保存该结构的实际数据。 将结构赋给新变量时,将复制该结构。 因此,新变量和原始变量包含同一数据的两个不同的副本。 对一个副本的更改不影响另一个副本。

       如:对例1中定义的结构作如下操作:

              //   使用结构

        static voidUseStruct()

        {

            DatelabourDay = new Date(5,1);

            Date yaoMingBirth = labourDay;

           labourDay.ReadDate();

           yaoMingBirth.ReadDate();

           yaoMingBirth.Change(1980, 11, 8);

           labourDay.ReadDate();

            yaoMingBirth.ReadDate();

        }

    显示结果:修改其中一个变量的值,不会影响和它同一个拷贝的另外一个变量。

    

 

       类是引用类型数据,存储在堆(heap)中和栈(stack)中,堆中存储的是真实的数据,栈中存储的是数据在堆中的地址。就像是在仓库里面放苹果,我们把放苹果的箱子编号,再将编号记录在一个本子上面,这个本子就是栈,仓库就是堆。 

       如:对例1中定义的类进行如下操作:

         //  使用类

        static voidUseClass()

        {

            Birthday myBirth =newBirthday(1987, 6, 12);

            Birthday liliBirth = myBirth;

           myBirth.ReadDate();

            liliBirth.ReadDate();

           myBirth.Change(1990, 3, 16);

           myBirth.ReadDate();

           liliBirth.ReadDate();

 

        }

    显示结果:修改一个引用的值,另一个相同的引用的值就会发生改变。

    

 

区别二:继承性

       大家都知道,类是可以继承的,它可以继承其他的;类或者接口,也可以被继承,并且,类的许多特性是通过继承来展现的,要阻止类的继承,必须显示的声明sealed。

结构没有继承:它不能继承另一个结构或者类,也不能被继承。也正因为如此,结构不能有抽象成员。虽然结构没有明确的用sealed声明,可是结构是隐式的sealed

但是,结构能够继承接口,方法和类继承接口是一样的:

例如:结构实现接口

  interface IImage

        {

            void Paint();

        }

 

        struct Picture : IImage

        {

            public void Paint()

            {

                // painting code goes here

            }

            private int x, y, z; // other struct members

        }  

  

区别三:初始化

       类可以声明的时候就初始化。如:

       class Birthday

    {

        int year;

        byte month;

        byte day;

        int count = 5;

}

但是同样的语句用到struct结构中就会发生错误,因为结构中不能在声明一个变量的时候就初始化:

注意,结构在声明全局变量的时候是可以在声明时就初始化的

区别四:构造函数

类和结构都有自己的默认构造函数(默认构造函数不带参数),也都可以编写自己的带参数的构造函数,编写的方法都一样:构造函数没有返回值,并且名称与类(或者结构相同)。但是他们是有区别的:

在类中,一旦我们编写了带参数构造函数,默认构造函数就不存在了。当我们要调用不带参数的构造函数来初始化对象时,我们必须再自己编写一个不带参数的构造函数。

如:例1 中类Birthday的构造函数:

//   构造函数:0在日期中是没有意义的,用来表示用户没有输入

        public Birthday(intyy,byte mm,bytedd)

        {

            this.year = yy;

            this.month = mm;

            this.day = dd;

        }

        public Birthday(intyy,byte mm)

            : this(yy, mm, 0)

        {

        }

        public Birthday(intyy)

        {

            this.year = yy;

    }

但是在结构中,始终存在一个不带参数的默认构造函数,并且,这个构造函数是不可替代的,不能重写,也不能覆盖,所以,下面操作是错误的:

public override Date()

        {

            this.year = 1987;

            this.month = 6;

            this.day = 12;

   }

在结构中,我们只能编写带参数的构造函数,不能编写不带参数的构造函数。

 

 

在类中,声明构造函数的时候我们可以不用初始化所有的字段,系统的(runtime机制)会将我们忽略的字段自动初始化为该类型的零值(0,null,true)

如,类birthday:我们用构造函数:

public Birthday(int yy)

        {

            this.year = yy;

    }

声明一个对象,系统会自动将月日初始化为0,显示的时候就只有年。

但是在结构中,我们自己编写的构造函数必须显式的为结构的所有字段赋值,否则会发生编译错误。例如,我们将构造函数稍微改动一下:

public Date(byte mm,bytedd)           

        {

            this.month = mm;

            this.day = dd;

   }

编译运行不会成功,显示错误: Field 'StructAndClass.Date.year' must be fully assigned before controlis returned to the caller

区别五:析构函数

       类有析构函数,我们都知道,但是结构是没有析构函数的。

       假如我们为结构Date添加析构方法:

       ~Date()

        {

            Console.WriteLine("谁说结构没有析构方法?");

            Console.ReadKey();

        }

    编译会弹出错误:

    Only class types cancontain destructors

区别六:关键字

1、在类中可以使用但是在结构中限制使用的关键字有:abstract、sealed、protected;

2、Static关键字可以用在类名前面用来声明静态类,但是不能用在struct前面,不存在静态结构。

类和结构的使用选择:

类通常用于对较为复杂的行为建模,或对要在创建类对象后进行修改的数据建模。结构最适合一些小型数据结构,这些数据结构包含的数据以创建结构后不修改的数据为主。

 

何时该用struct、何时该用class:

 

1、 大多数情况下该类型只是一些数据时,该类型的行为类似于基于类型,结构式最佳的选择,否则用class

2、 在表示抽象或者多层次的数据的时候,类是最好的选择:

3、 该类型不要继承自任何类型,否则用class

4、 该类型不要继承自任何类型,否则用class

5、 该类型的实例不会频繁地用于方法的参数传递,否则用class

6、 该类型的实例不会被频繁地用于诸如ArrayList、Hashtable之类的集合中,否则用class struct表示如点、矩形和颜色这样的轻量对象,例如,如果声明一个含有 1000 个点对象的数组,则将为引用每个对象分配附加的内存,在此情况下,结构的成本较低;当struct变得很大时,应该用class:堆栈的空间有限,对于大量的逻辑的对象,创建类要比创建结构好一些。

参考:

1、一览表:

 

2、结构和类的区别:

http://msdn.microsoft.com/zh-cn/library/ms173109.aspx

3、结构和类的异同:

http://hi.baidu.com/loveastyy/blog/item/f15601e8eb771938b80e2da1.html

4、源代码:

Struct,cs

 

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace StructAndClass

{

    struct Date

    {

        int year;

        byte month;

        byte day;

 

        //   构造函数

        public Date(int yy,byte mm,byte dd)

        {

            this.year = yy;

            this.month = mm;

            this.day = dd;

        }

        public Date(byte mm,byte dd)           

        {

            //   0年是没有意义的,用来做节日的标记

            this.year = 0;

            this.month = mm;

            this.day = dd;

        }

        public Date(int yy)

            : this(yy, 1, 1)

        {

        }

      

 

        //   读取日期时间

        public voidReadDate()

        {

            if (this.year == 0)

            {

                Console.WriteLine("{0}-{1}",this.month,this.day);

            }

            else

            {

                Console.WriteLine("{0}-{1}-{2}",this.year,this.month,this.day);

            }

        }

 

        //   修改日期时间

        public void Change(int yy, byte mm, byte dd)

        {

            this.year = yy;

            this.month = mm;

            this.day = dd;

        }

        public void Change(byte mm, byte dd)

        {

            Change(0,mm, dd);

        }

       

 

        //   重写ToString()方法

        public override string ToString()

        {

            string s = null;

            if (this.year == 0)

            {

                Console.WriteLine("{0}-{1}",this.month,this.day);

                s = this.month.ToString() +"-"+this.day.ToString();

            }

            else

            {

                Console.WriteLine("{0}-{1}-{2}",this.year,this.month,this.day);

                s = this.year.ToString() +"-"+this.month.ToString() +"-" +this.day.ToString();

            }

            return s;

        }

    }

}

 

 

 

Class.cs

 

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace StructAndClass

{

    class Birthday

    {

        int year;

        byte month;

        byte day;

 

        //   构造函数:0在日期中是没有意义的,用来表示用户没有输入

        public Birthday(intyy,byte mm,bytedd)

        {

            this.year =yy;

            this.month = mm;

            this.day = dd;

        }

        public Birthday(intyy,byte mm)

            : this(yy, mm, 0)

        {

        }

        public Birthday(intyy)

        {

            this.year = yy;

        }

 

        ~Birthday()

        {

            Console.WriteLine("没有任何争议,类是有析构方法的!");

            Console.ReadKey();

        }

 

        //   读取生日时间

        public voidReadDate()

        {

            if (this.month == 0)

            {

                Console.WriteLine("{0}",this.year);

            }

            else if (this.day == 0)

            {

                Console.WriteLine("{0}-{1}",this.year,this.month);

            }

            else

            {

                Console.WriteLine("{0}-{1}-{2}",this.year,this.month,this.day);

            }

        }

 

        //   修改生日时间

        public void Change(int yy,byte mm,byte dd)

        {

            this.year = yy;

            this.month = mm;

            this.day = dd;

        }

        public void Change(int yy, byte mm)

        {

            Change(yy,mm, 0);

        }

        public void Change(int yy)

        {

            Change(yy,0, 0);

        }

  

 

        //   重写ToString()方法

        public override string ToString()

        {

            string s = null;

            if (this.month == 0)

            {

                Console.WriteLine("{0}",this.year);

                s = this.year.ToString();

            }

            else if (this.day == 0)

            {

                Console.WriteLine("{0}-{1}",this.year,this.month);

                s = this.year.ToString() +"-"+this.month.ToString();

            }

            else

            {

                Console.WriteLine("{0}-{1}-{2}",this.year,this.month,this.day);

                s = this.year.ToString() +"-"+this.month.ToString() +"-" +this.day.ToString();

            }

            return s;

        }

    }

}

 

 

Program.cs

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace StructAndClass

{

    class Program

    {

 

        //   使用结构

        static voidUseStruct()

        {

            Date labourDay = newDate(5, 1);

            Date yaoMingBirth = labourDay;

           labourDay.ReadDate();

           yaoMingBirth.ReadDate();

            yaoMingBirth.Change(1980, 11, 8);

           labourDay.ReadDate();

           yaoMingBirth.ReadDate();

           

 

        }

 

 

        //   使用类

        static voidUseClass()

        {

            Birthday myBirth =newBirthday(1987, 6, 12);

            BirthdayliliBirth = myBirth;

           myBirth.ReadDate();

           liliBirth.ReadDate();

           myBirth.Change(1990, 3, 16);

           myBirth.ReadDate();

           liliBirth.ReadDate();

 

            Birthday bird = newBirthday(1988);

           bird.ReadDate();

 

        }

 

       

 

        //   程序入口

        static void Main(string[] args)

        {

            UseClass();

           UseStruct();

            Console.ReadKey();

        }

    }

}

 

 

原创粉丝点击