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

垃圾回收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)
    ...

// 使用 as
IPointy 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版)


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值