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