【C#与.NET程序设计】(6)- C#垃圾回收及接口类型

来源:互联网 发布:江宁淳化街道网络问政 编辑:程序博客网 时间:2024/06/06 07:34

垃圾回收GC

.NET的CLR管理着托管堆,所有存在于托管堆上的对象会在“将来的某一时刻”被垃圾回收器自动销毁。因此 C# 里没有 delete 关键字

  • 托管资源
    由CLR管理,运行于托管堆上。C#的绝大多数对象
  • 非托管资源
    不由CLR管理,如 Image Socket, StreamWriter, Timer, Tooltip, 文件句柄,GDI资源,数据库连接等
    非托管资源需要手动释放:Dispose (推荐方法)和 Finalize(本质是析构函数)
// 托管堆// new 返回的是一个指向托管堆上对象的引用// 也就是说,对象本身在托管堆上,refToMyCar 保存在栈上,是对象的一个引用Car refToMyCar = new Car();

延迟对象实例化

使用泛型类 Lazy<> 使对象的数据只有在使用时才被创建,达到节约资源,缓解GC压力的目的

// 一首歌曲class Song{    public string Artist {get; set;}    public string TrackName {get; set;}    public string TrackLength {get; set;}}// 播放器上所有歌曲class AllTracks{    private Song[] allSongs = new Song[10000];}// 播放器class MediaPlayer{    private AllTracks allSongs = new AllTracks();    public AllTracks GetAllTracks()    {        return allSongs;    }}// 调用时间接创建了10000个对象MediaPlayer myPlayer = new MediaPlayer();

使用 Lazy<> 修饰 MediaPlayer 的 AllTracks 类

// 使用 Lazy<> 延迟对象// 播放器class MediaPlayer{    private Lazy<AllTracks> allSongs = new Lazy<AllTracks>();    public AllTracks GetAllTracks()    {        return allSongs.Value;    }// 只有在调用函数 GetAllTracks 时,10000个对象才被创建myPlayer.GetAllTracks();

接口

接口就是一组抽象成员的集合,使用关键字 “interface”声明
可以把接口看做是抽象类的升级版:

  • 只有派生类才支持由抽象父类定义的成员,而接口更具独立性,所有类都可以用
  • 派生类必须实现抽象父类的所有抽象方法,不论实际是否需要;
    而接口没这个限制(但如果要用这个接口,就必须实现其所有方法
  • 接口更像是一种公共类的声明,只要有类要用就做具体实现
  • 接口可以作为输入参数类型,也可作为返回输出类型
// 自定义接口public interface IPointy{    // 只能定义方法    public int numbOfPoints;                        // 错误,不能有字段    public IPointy() {...}                          // 错误,不能有构造函数    byte GetNumberOfPoints(){return numbOfPoints;}  // 错误,不能提供实现    // 方法是隐式公共、抽象的    byte GetNumberOfPoints();                       // 正确    // 可以定义属性    byte Points{get;}                               // 正确}// 使用该接口// 与类继承不同,一个类可以接受多个接口class Triangle:Shape, IPointy{    // 实现 IPointy 方法    public byte GetNumberOfPoints()    {        return 3;    }    // 实现 IPoints 属性    public byte Points    {        get{return 3;}    }}

使用“is”和“as”确定接口是否被支持

如果不确定当前类是否重写了特定接口,可以使用“is”或“as”测试

// 使用 is Triangle triangle = new Triangle();if(triangle is IPointy)    ...// 使用 asIPointy itfpt = triangle as IPointy;if(itfpt != null)    ...

接口命名冲突

假设多个接口定义了同样名字和输入输出参数的方法,而一个类同时调用了这些接口,就会造成冲突
此时只能通过显式调用来明确接口(但这种方法是隐式私有的

// 不同接口定义同样的方法public interface IDrawToFrom{    void Draw();}public interface IDrawToMemory{    void Draw();}public interface IDrawToPrinter{    void Draw();}// Octagon 类支持所有接口class Octagon:IDrawToFrom, IDrawToMemory, IDrawToPrinter{    // 按常规方法会导致混乱    public void Draw()    {...}    // 显式绑定 Draw    // 隐式私有,不能提供修饰符(public)    void IDrawToFrom.Draw()    {...}    void IDrawToMemory.Draw()    {...}    void IDrawToPrinter.Draw()    {...}}

接口层次结构

与类的层次结构类似,接口也可以对父接口做继承

// 接口的继承public interface IDrawable{    void Draw();}// IAdvancedDraw 继承了 IDrawable 的 Draw() 方法public interface IAdvancedDraw:IDrawable{    void DrawInBoundingBox();}

可枚举类型构建(IEnumerable)

如果要对自定义类型实现枚举,可以通过 IEnumerable 接口实现
有2种方式:

  • 委托请求到 System.Array (实现了枚举)
  • 使用 yield 构建迭代器
// 委托请求到 array// 添加枚举接口支持using System.Collections;public class Garage:IEnumerable{    private Car[] carArray = new Car[4];    public Garage()    {        carArray[0] = new Car();        carArray[1] = new Car();        carArray[2] = new Car();        carArray[3] = new Car();    }    public IEnumerator GetEnumerator()    {        // 返回数组对象的 IEnumerator        return carArray.GetEnumerator();    }}// 这样就可以用 foreach 枚举 Garage 里的每个 Car 对象了Garage carLot = new Garage();foreach(Car c in carLot){...}

yield 的方法更灵活,用户甚至可以自定义顺序

// yield 构建迭代器using System.Collections;public class Garage{    private Car[] carArray = new Car[4];    // 迭代器方法    public IEnumerator GetEnumerator()    {        foreach (Car c in carArray)            yield return c;    }    // 或者    // yield 构建命名迭代器    public IEnumerator GetTheCars(bool RetuenRevesed)    {        // 逆序        if(RetuenRevesed)        {            for(int i=carArray.Length; i!=0; i--)                yield return carArray[i-1];        }        else        {            //正序            foreach(Car c in carArray)                yield return c;        }    }}

可克隆对象构建(ICloneable)

由于类是引用类型,简单的赋值操作是浅复制,如果要实现深度拷贝,需要实现 ICloneable 接口

// 可克隆接口public class PointDescription{    public string PetName{get; set;}    public Guid PointID{get; set;}    public PointDescription()    {        PetName = "No-name";        PointID = Guid.NewGuid();  // 全局唯一标识符    }}public class Point : ICloneable{    ...    public PointDescription desc = new PointDescription();    // ICloneable 接口实现    public object Clone()    {        // MemberwiseClone 浅复制        // 数值类型可以 clone 过来        Point newPoint = (Point)this.MemberwiseClone();        // PointDescription 是引用类型,需要单独实现        PointDescription currentDesc = new PointDescription();        currentDesc.PetName = this.desc.PetName;        newPoint.desc = currentDesc;        return newPoint;    }}

可比较对象的构建(IComparable)

IComparable 接口指定了一种允许一个对象可基于某些特定值进行排序的行为

// 接口定义了一个对象与类似对象的关系public interface IComparable{    int CompareTo(object o);}

自定义类型需要实现 IComparable 接口以支持比较

// 自定义类型的比较public class Car : IComparable{    public int CarID{get; set;}    public string PetName{get; set;}    ...    // IComparable 的实现    int IComparable.CompareTo(object obj)    {        // 比较 CarID        // 方法1        if(obj is Car)        {            if(this.CarID > temp.CarID)                return 1;            if(this.CarID < temp.CarID)                return -1;            else                return 0;        }        // 方法2,因为 Int 类型已实现 IComparable        if(obj is Car)            return this.CarID.CompareTo(obj.CarID);    }}// 排序Car[] myAutos = new Car[4];...Array.Sort(myAutos);

还有一种方式是实现 IComparer 接口,其定义了一个 compare 方法
但一般需要专门实现一个辅助类来实现比较

// 辅助类public class PetNmaeComparer : IComparer{    int IComparer.Compare(object o1, object o2)    {        if(o1 is Car && o2 is Car)        {            return String.Compare(o1.PetName, o2.PetName);        }    }}// 使 Car 类同时支持 PetName 的比较Car[] myAutos = new Car[4];...Array.Sort(myAutos, new PetNmaeComparer());

参考

【1】C#与.NET 4高级程序设计(第5版)


原创粉丝点击