写了那么多年C#,但一直不知道的事儿系列之1

《C#与.NET 4高级程序设计》

1、??操作符,跟可空类型配合,当获得的值是null时,可以用??操作符给一个可空类型赋值,比传统的if else写法更紧凑, pamaTest ?? 100; 意味着,如果可空变量pamaTest为null,则赋值为100;

2、this关键词,使用this进行串联构造函数调用,如:

public Motorcycle() { }
public Motorcycle(int intintensity) :this(intintensity,"") { }

public Motorcycle(int intintensity,string name)
{
  //执行
}

3、static 定义静态构造函数,可以保证不管创建多少个对象,静态数据只被分配一次,安全地设置静态数据的值。注意,一个类只能有一个静态构造函数,且不接受任何参数;

4、自动属性在定义时必须支持读写功能,可以定义为更严格的get 或set;自动属性就是{get;set;},虽然会用,但是之前不知道叫什么...

5、对象初始化器语法,目的简化新建对象的过程,可以与现有构造函数配合使用,达到快速创建对象并设置一些属性和公共字段。使用方法:

//X、Y分别是类Point的公共属性
//显式调用
Point finalPoint = new Point(){ X=30, Y=30};
//隐式调用
Point finalPoint = new Point{ X=30, Y=30};

//调用自定义构造函数
Point finalPoint = new Point(PintColor.Gold) { X=30, Y=30};

6、常量和只读字段:常量和只读字段一样,常量只读字段不能在赋初值之后改变,常量是隐式静态的,定义常量时必须为常量指定初始值(因为构造函数是在运行时调用),只读字段的值可以在运行时决定,因此可以在构造函数作用域中进行赋值(其他地方不行);静态只读字段,加上static关键字,成为静态的,它的赋值可以在定义时初始化,或在静态构造函数中初始化;

7、partial 分部类型,分部类的每个部分必须都用partial关键字标记,且相同参数的构造函数只允许在所有分部类中出现一次;要求:类型名称(类名)和命名空间相同;

8、继承类,派生类可以继承父类的除构造函数以外的所有函数,即在创建一个派生类对象的时候,会先执行父类的构造函数,然后再执行派生类的构造函数;

9、sealed关键字,防止发生继承;

10、base关键词,控制基类的创建;在派生类的构造函数中,加入base关键词,可以显示调用合适的自定义基类构造函数而不是默认构造函数;

//指定调用父类符合条件的构造函数
public ChildrenClass(string fullname,int year): base(fullname)
{
     this.year = year;
}

11、virtual 和 override关键字的区别,virtual用于当基类可以(不是必须的)由子类重写,则必须用virtual关键字,称为虚方法;如果子类希望改变虚方法的实现细节,则必须用voerride关键字。

12、抽象类和抽象方法,当我们想要避免某个基类被实例化,则可以使用abstract关键字,让其称为抽象类;当我们基类里的方法必须强制所有之类来定义如何被呈现,则使用abstract关键字,使该方法成为一个抽象方法。

13、强制转换(隐藏基类字段/方法/属性/常量等),当我们的子类与基类中有相同的方法,其中,基类的方法又不是虚方法(使用的又是第三方库,没有源代码情况),又不想改变我们子类的方法时,可使用new 关键字强制转换/隐藏父类。

//隐藏任何在我之上的PetName属性
public new string PetName {get; set;}

//隐藏任何在我之上的Draw()实现
public new void Draw(){
    Console.WriteLine("Drawing A");
}

14、is 和as 关键字,类类型之间强制转换的第一条准则就是如果两个类通过“is-a”关系关联,在基类引用中保存派生类类型总是安全的,称之为隐式转换,因为它是基于继承的规则。通过(ClassWantToCastTo)方式,则称为显示强制转换,是在运行时而不是编译时进行运算,因此当出现代码错误时,在编译时无法及时发现错误,因此可以通过as 或 is关键字进行检查,两者区别是,is关键字返回false或true,as 则当不是转换时,返回的是null。

15、System.object超级父类,核心:

  • Equals():默认情况下,如果被比较的项指向内存中同一个项,则方法会返回true。因此,用于比较对象引用,则不是对象的状态,一般情况下会被重写为对比属性值,同时还需要重写GetHashCode()方法;
  • GetHashCode():这个方法返回int来标识指定的对象实例;改写时可以根据基于唯一字符串数据点返回散列代码,如return SSN.GetHashCode();其中SSN是唯一值的属性名;
  • ToString():返回对象的字符串表示(默认是<namespace>.<type name>格式),一般可以改写为键值对的方式把属性名和内容包装成一个字符串,并通过重写Equals()方法合作,快速进行对象的对比。
  • MemberwiseClone():这个方法的作用是逐个成员地返回当前对象的副本,通常用于克隆对象。

除此之外,object有一个静态方法,ReferenceEquals(),用于当重写Equals()方法后,想要对比两个对象是否指向同一个内存块时,可以使用object.ReferenceEquals(obj1,obj2)进行对比。

16、异常,分为系统级异常(System.SystemException,.NET平台引发的异常被称为系统异常)和应用程序级异常(System.ApplicationException)。自定义异常应该继承自应用级异常,并且所有异常类应以“Exception”后缀结束。

通常情况下,需要在出现错误的类与该错误关系紧密时才需要创建自定义异常。try catch处理多个异常时,catch异常原则:最前面的catch捕获最特定的异常,最后面的捕获最普通的异常。

17、拦截并处理“损坏状态异常”(即CSE,Corrupted State Exception),使用[HandledProcessCorruptedStateExceptions]特性,如果发生CSE错误,在程序终止之前我们可以在捕获System.Exception的catch块中进行某些处理。

18、类、对象和应用,定义一个类之后,使用new 关键字分配任意数量的对象,new之后返回的是一个指向堆上对象的应用,而不是真正的对象本身,在方法作用域中讲应用变量声明为本地变量,这个引用变量保存在栈内,以供应用程序以后使用。

19、只有在创建类的实例时,才会产生堆的分配。托管堆保存着一个指针,它精确地指示下一个对象将被分配的位置。

20、C#中将引用设为null意义不大,因为将引用赋值为null,并不意味着强制垃圾回收期立即启动并把对象从堆上异常,仅仅只是显示取消引用和之前引用所指向对象之间的连接。

21、垃圾回收器:在一次垃圾回收过程中,运行库将检查托管堆上的对象,判断应用程序否仍然可访问它们,CLR通过建立一个对象图,代表堆上可达的每一个对象(同一个对象不会在一个对象图中出现2次,避免了循环引用计数)。当CLR试图寻找不可访问的对象时,为了提高效率,不会逐一检查托管堆的每一个对象,通过对堆上的每个对象指定一个属于某“代”(generation)的方式实现。设计思路:对象在堆上存在的时间越长,则更可能应该保留。因此基于这个假设,每一个对象都属于下列某代,0代:从来没有被标记为回收的新分配的对象;1代:在上一次垃圾回收中没有被回收的对象,2代:在一次以上的垃圾回收后仍然没有被回收的对象,(0代和1代称为暂时代)。垃圾回收器首先检查所有的第0代对象(幸存的第0代对象提升为第1代),若清理后仍需更多的内存则检查第1代对象(没有被回收的第1代对象随后被提升到第2代),若清理后内存仍然不够,则检查第2代。

22、强制垃圾回收,使用场景:应用程序将要进入一段代码且不希望被可能的垃圾回收中断或应用程序刚刚分配非常多的对象,你想尽可能地删除已获得的内存。

//强制一次垃圾回收,并等待每一个对象都被终结
GC.Collect();
//也可以只检查第0代的对象
GC.Collect(0);
GC.WaitForPendingFinalizers();

手动强制垃圾回收时,必须嗲用GC.WaitForPendingFinalizers(),该方法会在回收过程中挂起调用的“线程”,保证代码不调用当前正在被销毁的对象的方法。

23、Finalize()方法的作用是保证.NET对象能在垃圾回收时清理非托管资源。重写Finalize()的唯一原因是,C#类通过PInvoke或复杂的COM互操作性任务使用了非托管资源(一般情况下是通过System.Runtime.InteropServices.Marshal类型定义的各成员,如数据库和文件句柄等)。

24、Finalize()和Dispose()区别:

  • 结构类型和类类型都可以实现IDisposable,而只有Finalize()只适用于类类型;
  • Dispose()方法不只负责释放一个对象的非托管资源,还对任何它包含的可处置对象调用Dispose(),在Dispose()方法中与其他托管对象通信是很安全的。
  • 重写了Finalize()始终是安全的,可以不需要用户参与竞选垃圾回收。但是如果调用者忘记调用Dispose(),非托管资源可能会永远留在内存中。
public class MyResourceWrapper : IDisposable
{
        //如果对象忘记调用Dispose(),垃圾回收器会调用这个方法
        ~MyResourceWrapper()
        {
            //清除所有内部的非托管资源
            //不要调用任何托管对象的Dispose()
        }

        //对象用户调用这个方法来尽快清除资源
        public void Dispose()
        {
            //在这里清理费托管资源
            //在其他包含的可处置对象上调用Dsipose()

            //如果用户调用了Dispose()就不需要终结,因此跳过终结
            GC.SuppressFinalize(this);
        }
}

上面的代码有重复代码,增加了维护复杂性,下面是改写后的版本:

public class MyResourceWrapper : IDisposable
    {
        //用来判断Dispose()是否被调用
        private bool disposed = false;
        ~MyResourceWrapper()
        {
            //调用辅助方法
            //指定false表示GC触发了清理过程
            cleanUp(false);
        }

        //对象用户调用这个方法来尽快清除资源
        public void Dispose()
        {
            //调用辅助方法
            //指定true表示对象触发了清理过程
            cleanUp(true);
            //现在跳过终结
            GC.SuppressFinalize(this);
        }

        private void cleanUp(bool disposing)
        {
            //保证我们还没有被处置
            if (!this.disposed)
            {
                //如果disposing等于true,释放所有托管的资源
                if (disposing)
                {
                    //释放托管的资源
                }
                //在这里清理非托管的资源
            }
            disposed = true;
        }
    }
  • 如果对象支持IDisposable,总是要对任何直接创建的对象调用Dispose(),因此可以使用C#的using关键字,当退出using作用域时,自动调用Dispose(),同时,using 作用域中可以声明相同类型的多个对象。
//使用逗号分隔的列表来声明要释放的多个对象
using (MyResourceWrapper rw = new MyResourceWrapper(),
                         rw2 = new MyResourceWrapper())
{
    //使用rw 和rw2
}

注:Dispose需要实现IDisposable接口的Dispose()方法。

25、延迟初始化Lazy,Lazy<>泛型,保证在代码库实际使用它之前不会被创建,由于是一个泛型类,因此第一次使用时必须制定要创建的项的类型。延伸:泛型委托,System.Func<>,它指向的方法的返回值类型与相关的Lazy<>变量所创建的类型是相同的。为了简化Func<>的用法,建议使用Lambda表达式。在获取实际存储的数据时,我们必须使用Lazy<>类的只读属性Value。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值