C#中的结构体与类的区别

转自:https://www.cnblogs.com/hig

经常听到有朋友在讨论C#中的结构与类有什么区别.正好这几日闲来无事,自己总结一下,希望大家指点.

1. 首先是语法定义上的区别啦,这个就不用多说了.定义类使用关键字class 定义结构使用关键字struct.在语法上其实类和结构有着很多相似的地方.

   定义类的语法    

 1 class Person
 2 {
 3    private string name;
 4    private int age;
 5    
 6    public void SayHi()
 7    {
 8        Console.WriteLine("Hello,My Name is "+this.name+",My Age is "+this.age);
 9    }
10 }

    定义结构的语法. 

 1     struct Rectangle
 2     {
 3         private int width;
 4         private int height;
 5          
 6         public int GetArea()
 7         {
 8             return this.width * height;
 9         }
10     }

    从语法上来看.它们的语法都大同小异,类里面的成员几乎都可以定义在结构体中,但是析构函数除外.这是为什么呢?后面解答.

2. 虽然我们说它们的语法极其相似,但是它们在语法还是有几点区别的.

   a.在结构体中可以声明字段,但是声明字段的时候是不能给初始值的.所以当我们试图这样写代码的时候,C#编译器在将源代码编译成程序集的是会提示语法错误.

       

   我们知道如果我们在类中声明1个字段的同时给这个字段赋初始值,这样是可以滴,就像下面这样. 

class Person
{
   private string name ="jack";
}

    但是如果像下面这样确实不行滴.声明完1个字段,再为这个字段赋值,就像下面这样. 

1 class Person
2 {
3    private string name;
4    name="jack";
5 }

    所以我们说,在类下面只能直接定义类的成员,只能定义.  比如定义成员字段,属性 方法 构造函数等等.上面那样的代码name="jack"这样的代码我们称之为“执行代码”,意思就是说这些代码只有在被执行的时候才会有效果.而你试想一下,那么这些代码什么时候被执行呢? 创建类的对象的时候? 那还用得着构造函数吗? 经常看到一些初学者在类的下面直接写这样代码.

 

    但是又有人会说了.诶, 那么为什么在声明类的字段的时候可以赋值呢?赋值表达式也是1个执行代码啊?为什么这样就不报错呢?给你看看下面的代码 你就会知道其中的真相了.

     

     当我们使用C#编译器将这段代码编译为程序集的时候,看看微软为我们生成的代码吧.

     

     展开构造函数,看看这里面有什么蹊跷吧!

     

     是的,C#编译器在编译的时候,如果我们声明字段的时候为字段赋值,那么为字段赋值的代码C#编译器在编译的时候会将赋值的代码放到构造函数中去,其实严格意义上来说,类的字段也是不能有初始值的.只不过微软在背后帮我们做了点事情,我们不知道而已.

     所以,不管在类和结构中,执行代码一定要写在方法中.不能直接写在结构或者类的下面.因为当执行代码写在方法中了,那么这些执行代码的执行时机才可以确定,就是这个方法被调用的时候了.

    从上面的内容,我们可以看出.其实从本质上来说,类和结构的字段都是不能有初始值的.只不过微软在语法上允许我们在定义类的字段的时候为其赋值.但是背后微软其实是把赋值的执行代码放到构造函数中去执行的. 而结构体微软却不帮我们这样做.至于这其中是什么原因.查了些资料,也看了园子里其他博友的文章,感觉都不能说服我,但是自己也想不出1个确切的理由微软为什么要这样做.那就先放着吧,希望参透其中原理的童鞋能指点.

 

    b. 关于构造函数.

    首先,关于隐式构造函数.我们知道,在1个类中如果我们没有为类写任意的构造函数,那么C#编译器在编译的时候会自动的为这个类生成1个无参数的构造函数.我们将这个构造函数称之为隐式构造函数 但是一旦我们为这个类写了任意的1个构造函数的时候,这个隐式的构造函数就不会自动生成了.

    在结构中,就不是这样了,在结构中隐式的构造函数无论如何都存在.看看代码吧.

    在下面的代码中 我们为结构体写了1个带参数的构造函数.如下.

    

    我们使用new关键字来创建结构体对象,我们发现调用构造函数的时候,提示是有两个构造函数的.多了1个无参数的构造函数.

     

   那么 我们再想,能不能手动的写1个无参数的构造函数呢?我们怀着无比激动的心情,试一下.

   

  结果是华丽丽的报错了.所以我们得出结论. 隐式的无参数的构造函数在结构中无论如何都是存在的,所以程序员不能手动的为结构添加1个无参数的构造函数.

  关于构造函数当然还不仅仅如此.我们知道在类的构造函数中我们可以写一些任意的代码(前提是符合C#语法啦),在结构体的构造函数中虽然也可以写任意的代码.但是C#语法规定在结构体的构造函数中,必须要为结构体的所有字段赋值.看看下面的代码吧.

   

   啊哦.....报错了.....

   我们也知道,在结构中还可以定义属性,所以有童鞋就这样写啦.看下面代码.

   

   这个错误,仍然提示我们在构造函数中没有为所有的字段赋值,这是很多童鞋遇到的问题,诶,不是要在构造函数中为所有的字段赋值么?我现在赋值了啊。为什么还是提示没有赋值呢? 我们在构造函数中为属性赋值 而属性又为字段赋值,为什么这样就不行呢? 原因很简单.因为语法要求我们为所有的字段赋值,虽然这里我们看得出来为属性赋值其实属性再把值赋值给字段, 我们说属性是对字段的操作,但是一定是这样的吗?我们完全可以在属性的set块里面什么都不写,如果什么都不写,那么属性还是在操作字段吗? 所以属性不一定是在操作字段的,在结构体的构造函数中我们为属性赋值,不认为是在对字段赋值,所以我们在构造函数中要直接为字段赋值.

  c.创建结构体对象的方式.

   创建结构体对象可以不使用new关键字.直接声明1个变量就可以.但是这样的话,结构体对象中的字段是没有初始值的,所以在使用字段之前必须要为这个字段赋值.

   

   原因很简单.因为声明的时候就不能给初始值,虽然构造函数中为对象的字段赋值,但是此种方式创建结构体对象,没有调用构造函数,所以必须要程序员在使用之前手动赋值。下面这样就可以了.

   

  另外1种创建结构体对象的方式和类一样,使用new关键字来创建,与不使用new关键字创建不同的是,通过使用new关键字创建结构体对象后,这个结构体对象的字段就已经有值了.原因不难理解,new关键字调用了构造函数,而结构体构造函数要求必须要为所有的字段赋值.

  

  所以,我们不难猜出.结构体的无参数的构造函数做了什么事情,在无参数的构造函数中为所有的字段赋值,值类型的字段赋值0,给引用类型的字段赋值null.

  d. 结构体不能从另外1个结构或者类继承,但是可以实现接口.特殊的是.虽然结构不能从别的类或者结构继承,但是所有的结构都默认从ValueType类继承,ValueType类再从Object类继承.所以结构体对象仍然拥有超类Object的成员.看看下面的微软生成的代码就知道了.

    

3. 它们之间最大的区别 是结构体是值类型 类是引用类型.

   结构体是值类型,当其作为1个局部变量的时候,变量是存储在栈空间中的,其对象的字段直接存储在这个变量中的.就像下面这样.

   

   与引用类型的类不一样,引用类型的变量中存储的是对象在堆空间中的地址,所以当我们传递1个引用类型的变量的时候,其实传递的是变量的值(对象的地址) 传递完以后 对变量的修改会影响到另外1个变量指向的对象的值.

 

4. 最后 谈一下什么时候使用结构,什么使用类.

   我们知道,结构存储在栈中,而栈有1个特点,就是空间较小,但是访问速度较快,堆空间较大,但是访问速度相对较慢.所以当我们描述1个轻量级对象的时候,可以将其定义为结构来提高效率.比如点,矩形,颜色,这些对象是轻量级的对象,因为描述他们,只需要少量的字段。当描述1个重量级对象的时候,我们知道类的对象是存储在堆空间中的,我们就将重量级对象定义为类. 他们都表示可以包含数据成员和函数成员的数据结构。与类不同的是,结构是值类型并且不需要堆分配。结构类型的变量直接包含结构的数据,而类类型的变量包含对数据的引用(该变量称为对象)。 struct 类型适合表示如点、矩形和颜色这样的轻量对象。尽管可能将一个点表示为类,但结构在某些方案中更有效。在一些情况下,结构的成本较低。例如,如果声明一个含有 1000 个点对象的数组,则将为引用每个对象分配附加的内存。所以结构适合表示1个轻量级对象.

   基于另外1个理由我也会使用结构. 我们在变量传值的时候,我就是希望传递对象的拷贝,而不是对象的引用地址,那么这个时候也可以使用结构了。

 

例题1、两人对战游戏

struct Ren
        {
            public string Name;
            public int Blood;
            public int Attack;
            public int Defend;
            public int Quick;
            
        }
        struct WuGong
        {
            public string Name;
            public int Attack;
        }
        static void Main(string[] args)
        {
            WuGong[] wg = new WuGong[3];
            wg[0].Name = "辟邪剑法";
            wg[0].Attack = 500;
            wg[1].Name = "降龙十八掌";
            wg[1].Attack = 600;
            wg[2].Name = "暗然消魂掌";
            wg[2].Attack = 400;
    
            Ren r1 = new Ren();
            Ren r2 = new Ren();
            //初化两个战斗人员的属性。
            Console.Write("请输入第一个战士姓名:");
            r1.Name = Console.ReadLine();
            Console.Write("请输入第二个战士姓名:");
            r2.Name = Console.ReadLine();
            //生成血量
            Random rand = new Random();
            r1.Blood = rand.Next(1000) + 1000;
            r2.Blood = rand.Next(1000) + 1000;
            //生成攻防
            r1.Attack = rand.Next(50) + 50;
            r2.Attack = rand.Next(50) + 50;
            r1.Defend = rand.Next(50) + 50;
            r2.Defend = rand.Next(50) + 50;
            //生成身法
            r1.Quick = rand.Next(100);
            r2.Quick = rand.Next(100);

            Console.WriteLine("姓名:" + r1.Name + "\t生命力:" + r1.Blood+"\t攻击力:"+r1.Attack+"\t防御力:"+r1.Defend+"\t敏捷度:"+r1.Quick);
            Console.WriteLine("VS");
            Console.WriteLine("姓名:" + r2.Name + "\t生命力:" + r2.Blood + "\t攻击力:" + r2.Attack + "\t防御力:" + r2.Defend + "\t敏捷度:" + r2.Quick);
            Console.WriteLine("按任意键开始...");
            Console.ReadKey();
            while(true)
            {
                //跳出循环。
                if(r1.Blood <=0 && r2.Blood<=0)
                {
                    Console.WriteLine(r1.Name+"与"+r2.Name+"同归于尽了!");
                    break;
                }
                if(r1.Blood<=0)
                {
                    Console.WriteLine(r2.Name+"把"+r1.Name+"KO了~!");
                    break;
                }
                if (r2.Blood <= 0)
                {
                    Console.WriteLine(r1.Name + "把" + r2.Name + "KO了~!");
                    break;
                }

                //对战
                
                //大招
                int dz1 = rand.Next(10); //r2的大招

                if (dz1 >= 8) //大招
                {
                    WuGong w1 = wg[rand.Next(3)];

                    int b1 = rand.Next(100); //r1失掉的血
                    int gj1 = rand.Next(100) - 50; //为了浮动r1的攻击
                    int fy1 = rand.Next(100) - 50;//为了浮动r2的防御
                    b1 = (b1 + (r2.Attack + gj1+w1.Attack) - (r1.Defend + fy1)) < 0 ? 0 : (b1 + (r2.Attack + gj1+w1.Attack) - (r1.Defend + fy1));
                    r1.Blood -= b1;
                    if (r1.Blood < 0)
                    {
                        r1.Blood = 0;
                    }

                    //稍待一下。
                    System.Threading.Thread.Sleep(2000);

                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine(r2.Name + "使用大招"+w1.Name+"发起攻击," + r1.Name + "失掉生命力" + b1 + "点!");
                    Console.ResetColor();
                    Console.WriteLine();
                   
                }
                else //平常招式
                {
                    int sf1 = rand.Next(80);
                    if (r1.Quick - sf1 >= 0)
                    {
                        Console.WriteLine(r1.Name + "躲过了" + r2.Name + "攻击");
                    }
                    else
                    {
                        int b1 = rand.Next(100); //r1失掉的血
                        int gj1 = rand.Next(100) - 50; //为了浮动r1的攻击
                        int fy1 = rand.Next(100) - 50;//为了浮动r2的防御
                        b1 = (b1 + (r2.Attack + gj1) - (r1.Defend + fy1)) < 0 ? 0 : (b1 + (r2.Attack + gj1) - (r1.Defend + fy1));
                        r1.Blood -= b1;
                        if (r1.Blood < 0)
                        {
                            r1.Blood = 0;
                        }

                        //稍待一下。
                        System.Threading.Thread.Sleep(2000);

                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine(r2.Name + "发起攻击," + r1.Name + "失掉生命力" + b1 + "点!");
                        Console.ResetColor();
                        Console.WriteLine();
                    }
                }
                //稍待一下。
                System.Threading.Thread.Sleep(2000);

                int dz2 = rand.Next(10); //r2的大招

                if (dz2 >= 8) //大招
                {
                    WuGong w1 = wg[rand.Next(3)];

                    int b2 = rand.Next(100); //r1失掉的血
                    int gj1 = rand.Next(100) - 50; //为了浮动r1的攻击
                    int fy1 = rand.Next(100) - 50;//为了浮动r2的防御
                    b2 = (b2 + (r1.Attack + gj1 + w1.Attack) - (r2.Defend + fy1)) < 0 ? 0 : (b2 + (r1.Attack + gj1 + w1.Attack) - (r2.Defend + fy1));
                    r2.Blood -= b2;
                    if (r2.Blood < 0)
                    {
                        r2.Blood = 0;
                    }

                    //稍待一下。
                    System.Threading.Thread.Sleep(2000);

                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine(r1.Name + "使用大招" + w1.Name + "发起攻击," + r2.Name + "失掉生命力" + b2 + "点!");
                    Console.ResetColor();
                    Console.WriteLine();

                }
                else
                {

                    int sf2 = rand.Next(80);
                    if (r2.Quick - sf2 >= 0)
                    {
                        Console.WriteLine(r2.Name + "躲过了" + r1.Name + "攻击");
                    }
                    else
                    {
                        int b2 = rand.Next(100);//r2失掉的血
                        int gj2 = rand.Next(100) - 50; //为了浮动r1的攻击
                        int fy2 = rand.Next(100) - 50;//为了浮动r2的防御
                        b2 = (b2 + (r1.Attack + gj2) - (r2.Defend + fy2)) < 0 ? 0 : (b2 + (r1.Attack + gj2) - (r2.Defend + fy2));
                        r2.Blood -= b2;
                        if (r2.Blood < 0)
                        {
                            r2.Blood = 0;
                        }

                        Console.ForegroundColor = ConsoleColor.Blue;
                        Console.WriteLine(r1.Name + "发起攻击," + r2.Name + "失掉生命力" + b2 + "点!");
                        Console.ResetColor();
                        Console.WriteLine();
                    }
                }
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write("姓名:" + r1.Name + "生命力:" + r1.Blood + "\t");
                Console.Write("姓名:" + r2.Name + "生命力:" + r2.Blood);
                Console.ResetColor();
                Console.WriteLine();
                Console.WriteLine();

            }

        }

例题2.输入学生成绩信息

struct Student
        {
            public string Name;
            public int No;
            public double ToltallGrd;
            public double Chinese;
            public double math;//快速移至下行尾
        }
        //输入学生成绩信息并排序
        static void Main(string[] args)
        {
            Student[] a = new Student[5];
            //输入成绩
            GetGrade(a);
            //根据总分排序
            Order(a);
            //输出学生成绩信息
            Output(a);

        }

        private static void Output(Student[] a)
        {
            for (int i = 0; i < a.Length; i++)
            {
                Console.WriteLine("姓名:{0}\t学号:{1}\t名次:\t{2}总分:{3}\t语文:{4}\t数学:{5}",
                    a[i].Name, a[i].No, i + 1, a[i].ToltallGrd, a[i].Chinese, a[i].math);
            }
        }

        private static void Order(Student[] a)
        {
            for (int i = 1; i <= a.Length - 1; i++)
            {
                for (int j = 1; j <= a.Length - i; j++)
                {
                    if (a[j].ToltallGrd > a[j - 1].ToltallGrd)
                    {
                        Student tem = a[j];
                        a[j] = a[j - 1];
                        a[j - 1] = tem;
                    }
                }
            }
        }

        private static void GetGrade(Student[] a)
        {
            for (int i = 0; i < a.Length; i++)
            {

                Console.WriteLine("请输入第" + (i + 1) + "名学生姓名:");
                a[i].Name = Console.ReadLine();
                a[i].No = i + 1;
                Console.Write("语文:");
                a[i].Chinese = Convert.ToDouble(Console.ReadLine());
                Console.Write("数学:");
                a[i].math = Convert.ToDouble(Console.ReadLine());
                a[i].ToltallGrd = a[i].Chinese + a[i].math;

            }
        }

捕获12

例题3.郭靖PK欧阳克

struct Roal
    {
        public int blood;
        public int def;
        public int mov1;
        public int mov2;

    }
    class 对战游戏
    {
        static void Main(string[] args)
        {
            Roal cast1, cast2;
            //角色1的属性
            cast1.blood = 1000;
            cast1.def = 50;
            cast1.mov1 = 120;
            cast1.mov2 = 160;
            //角色2的属性
            cast2.blood = 1000;
            cast2.def = 60;
            cast2.mov1 = 110;
            cast2.mov2 = 130;

            //颜色设置
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.BackgroundColor = ConsoleColor.White;
            Console.Clear();

            //输入操作
            //角色1攻击
            while(cast1.blood >0&&cast2.blood >0)
            {
            Console.WriteLine("请郭靖发招:a-降龙十八掌 s-打狗棒");
            string m = Console.ReadLine();
            Random n1 = new Random();
            if (m == "a")
            {
                int s = n1.Next(60);//角色2的防御值
                int s2 = cast1.mov1 - s;//攻击效果
                cast2.blood -= s2;//角色2血量减少
            }
            else if (m == "s")
            {
                int s = n1.Next(60);//角色2的防御值
                int s2 = cast1.mov2 - s;//攻击效果
                cast2.blood -= s2;//角色2血量减少
 
            }
            //角色2攻击
             Console.WriteLine("请欧阳克发招:k-蛤蟆功 l-九阴真经");
            string h= Console.ReadLine();
            Random i = new Random();
            if (h  == "k")
            {
                int s = i.Next(50);//角色1的防御值
                int s2 = cast1.mov1 - s;//攻击效果
                cast1.blood -= s2;//角色1血量减少
            }
            else if (h == "l")
            {
                int s = n1.Next(50);//角色1的防御值
                int s2 = cast1.mov2 - s;//攻击效果
                cast1.blood -= s2;//角色1血量减少
            }
            else
            {
                Console.WriteLine("发招失败");
            }
            Console.Clear();
            cast1.blood  = cast1.blood  < 0 ? 0 : cast1.blood ;//控制血量不要小于零
            cast2.blood = cast2.blood < 0 ? 0 : cast2.blood;//控制血量不要小于零
            Console.WriteLine ("郭靖血量:{0}",cast1.blood );
            Console.WriteLine("欧阳克血量:{0}",cast2.blood);
            }

            //输出对殴结果
            if (cast1.blood == 0 && cast2.blood == 0)
            {
                Console.WriteLine("同归于尽");
            }
            else if (cast1.blood ==0)
            {
                Console.WriteLine("欧阳克打败郭靖,抢走黄蓉");
            }
            else
            {
                Console.WriteLine("郭靖打败欧阳克,和黄蓉去了桃花岛");
            }


        }

捕获6

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值