结构中不能实例属性_C#-面向对象-结构和日期

08bce4b00e22ac24b0fb5d342f8272a0.png
https://zhuanlan.zhihu.com/p/93049840​zhuanlan.zhihu.com
ad5abecfc347a3fc9e4c3dadbabf6452.png

同学们是否还记得,我们在讲类型转换的时候,有过这样的写法:

int.Parse("23");

以前我们知道,Parse()是一个方法;现在我们可以想一想,Parse()究竟是谁的方法了。要知道答案,非常简单,光标停留在Parse()上,直接F12转到定义,结果我们就看到了:

340ac869ec0605c85b4c447d08c3bf12.png

这什么意思?我们看到了Parse()是Int32的一个静态方法。而Int32又是什么?它是一个

结构(struct)

结构是一个和类(class)非常非常相似的封装容器。它的成员和语法,和类几乎一模一样。除了上面的静态方法调用,还可以:

            //new一个结构实例,生成它的实例对象
            int age = new Int32();
            //调用结构的实例方法
            age.CompareTo(21);

但是,结构

  1. 能够实现接口,但不能继承其他结构和类,自然也不能被其他结构和类继承(这可能是结构较少使用的一大原因);
  2. 默认有一个无参构造函数,但不能显式声明。有参构造函数可以显式声明,声明的有参构造函数也不会“隐藏”默认的无参构造函数。但构造函数必须保证结构中的所有字段和自动属性被赋值。

我们可以自己创建一个结构,试一试:

    internal struct Bed   //源栈同学的床位
    {
        //public Bed(){}  //报错
        //报错:没有给_id赋值
        //public Bed(string room)
        //{
        //    Room = room;
        //}
        private int _id;
        internal string Room { get; set; }
        public bool HasBooked;
    }

你也许会奇怪为什么结构的构造函数要要求这么多?因为结构类型是 值类型 。而值类型要求其所有成员必须有值!如果使用结构默认的无参构造函数,结构会自动的给所有成员赋默认值;否则,这些所有成员都必须在有参构造函数中被赋值。

一个比较“奇怪”的语法可以帮助你记住这一点:

            Bed dream /*= new Bed()*/;     //所以wx没有指向任何对象
            dream.HasBooked = true;        //但wx.HasBooked可以直接被赋值
            Console.WriteLine(dream.HasBooked);    //想一想true值被存放到哪里了

如果Bed不是struct而是class的话,这样的代码是会报错的:因为引用类型变量dream无法存放HasBook的值。但当dream是struct的时候,dream.HasBooked是直接存放在dream中的,所以可以赋值。

注意如果注释掉上面第2行代码,你会发布无法通过编译,因为dream.HasBooked没有被赋值;但注释掉第2行代码的同时取消掉第1行代码的注释,你会发现又OK了:

            Bed dream = new Bed();     //new Bed()实际上给所有struct成员赋了默认值
            //dream.HasBooked = true;        
            Console.WriteLine(dream.HasBooked); 

同样因为结构是值类型,值类型的优势是直接存放在栈中,可以快速读取;但它的数据量不能太大,否则就会耗用栈空间,反而拖累性能。(引用类型的优势:只赋值地址不copy内容,传递时更快……)

另外,注意结构不能继承,所以诸多面向对象的特性它都不能使用,所以实际开发中用得并不多。

常用的结构都是C#里的内置(build-in)类型。int其实是Int32的别名,double是Double的别名,bool是Boolean的……所有关键字定义的内置类型,除了string和object,其他全部都是值类型,又被称之为简单类型。(详见:内置类型表 - C# 参考)

我们接下来要详细介绍的,是

DateTime,包括日期(年月日)和时间(小时分钟秒等)。.NET为我们提供了大量关于日期的属性和方法,以方便我们的开发。

演示F12查看metadata:……

最常用的包括:

1)构造函数:通过new DateTime()构造一个时间对象,可以指定年月日小时分钟秒等:

            //2019年12月1日
            DateTime date = new DateTime(2019, 12, 1);
            //2019年12月1日 19点52分24秒
            DateTime dateTime = new DateTime(2019, 12, 1, 19, 52, 24);

2)静态属性:DateTime.Now,获取当前时间

            Console.WriteLine(DateTime.Now);

3)实例属性:根据时间变量取得它的年月日(Day/Year/Month)等,通常和DateTime.Now 搭配使用:

            Console.WriteLine(DateTime.Now.Day);  //当月的第几日

4)实例方法:在现有时间基础上增减,比如AddDays()/AddMonths()/AddYears()……

5)ToString()方法:可以用于指定日期显示的格式。通常,我们使用字符串指定。用y代表year,用M(注意大写)代表month,用d代表day等,如下所示:

            Console.WriteLine(DateTime.Now.ToString(
                "yyyy年MM月dd日 hh点mm分ss秒"));
            //显示:2019年12月01日 07点59分14秒

另外,注意DateTime中已经出现了:

TimeSpan,它也是一个struct,表示的是一段时间,通常由两个日期相减获得。注意:

  • Timespan计量的最大单位是天(Days),没有年和月
  • 用Days/Hours/Minutes等取出的是Timespan中天/小时/分钟等 部分 的值
  • 用TotalDays/TotalHours/TotalMinutes等取出的才是Timespan代表的时间间隔换算成天/小时/分钟等的值
    TimeSpan span =
        new DateTime(2020, 8, 24, 3, 2, 12) -
        new DateTime(2019, 7, 12, 9, 0, 0);

    Console.WriteLine(span);              //408.18:02:12

    Console.WriteLine(span.Days);         //408
    Console.WriteLine(span.TotalDays);    //408.751527777778

    Console.WriteLine(span.Hours);        //18
    Console.WriteLine(span.TotalHours);   //9810.03666666667

    Console.WriteLine(span.Minutes);      //2
    Console.WriteLine(span.TotalMinutes); //588602.2

Timespan以后在学习缓存的时候会非常有用。

另外,阅读源代码的时候,我们还发现了这样的代码:

public static DateTime operator +(DateTime d, TimeSpan t);

这被称之为

运算符重载

实际上就是改变了运算符加号(+)的运算逻辑:本来加号是只能用于数值型类型的,DateTime显然不是数值,本是不应该能够使用加号的,然而:

            Console.WriteLine(DateTime.Now + new TimeSpan(5000)); 

这就是因为在DateTime类中,进行了上述运算符重载

任何一个类都可以进行运算符重载,比如我们的Student类:

其语法要求是:

  • 方法只能是public和static的
  • 必须要有一个关键字operator
  • 至少一个参数和返回值类型相匹配
  • 某些运算符有要求“成套”,比如重载了==,就必须重载 !=

另外,不是所有运算符都可以重载。具体可查看:可以重载的运算符

和运算符重载非常类似的,还有一种语法:

类型转换重载

前面我们知道,类型之间的强转,本来只能发生在

我们可以用一个类

通常我们不会主动使用运算符和类型转换重载,但随着.NET core的开源,我们将越来越多的遇到

每日单词

8fa929cd917c176c084d3f3e0fdb853d.png

作业

  1. 用代码证明struct定义的类型是值类型
  2. 源栈的学费是按周计费的,所以请实现这两个功能:
    1. 函数GetDate(),能计算一个日期若干(日/周/月)后的日期
    2. 给定任意一个年份,就能按周排列显示每周的起始日期,如下图所示:

cfdc836965e429347da4978be8143f5a.png

注意:先写测试用例,确保所有测试用例通过


感谢童鞋们的阅读!^_^

我就是:黑律师/包工头/创业狗/老码农……现在还是教书匠的大飞哥。

再次重申这个系列的目标是:

1)通俗易懂。2)实战为主。3)面向就业。

系列内容的完善需要你的反馈!

欢迎点赞和评论,以及加入我们的QQ交流群:326801052。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值