鼎鼎知识库
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

20.C#继承.md 5.4KB

继承的类型

  • 单重继承:表示一个类可以派生自一个基类。C#就采用这种继承。
  • 多重继承:多重继承允许一个类派生自多个类。C#不支持类的多重继承,但允许接口的多重继承。
  • 多层继承:多层继承允许继承有更大的层次结构。类B派生自类A。类C又派生自类B。其中,类B也称为中间基类,C#支持它,也很常用
  • 接口继承:定义了接口的继承。这里允许多重继承。

实现继承

  • 声明派生自另一个类的一个类,可以用下列语法: class MyDerivedClass:MyBaseClass { // }
  • 如果类也派生自接口,则用逗号分隔列表中的基类和接口: ``` public class MyDerivedClass:MyBaseClass,IInterfacel,IInterface2 { // }
- 如果类和接口都用于派生,则类总是必须放在接口的前面。
- 对于结构,语法如下(只能用于接口继承):

public struct MyDerivedStruct:IInterfacel,IInterface2 {

//

}

- 如果在类定义中没有指定的基类,C#编译器就假定System.Object 是基类。因此,派生自Object类(或使用object关键字),与不定义基类的效果是相同的。

class MyClass //implicitly derives from System.Object {

}

举个例子,无论是矩形还是椭圆,形状都有一些共同点;形状都有位置和大小。定义相应的类时,位置和大小应包含在Shape类中。Shape类定义了只读属性Position和Shape,它们使用自动属性初始化器来初始化

public class Position {

public int X{get;set;}
public int Y{get;set;}

} public class Size {

public int Width{get;set;}
public int Heigh{get;set;}

} public class Shape {

public Position Position{get;}=new Position();
public Size Size{get;}=new Size();

}


虚方法
- 把一个基类方发声明为virtual,就可以在任何派生类中重写该方法

public class Shape {

public virtual void Draw()
{
    WriteLine($"Shape with {Position}and{size}");
}

}

- 也可以把virtual 关键字和表达式体的方法(使用Lamda运算符)一起使用。这个语法可以独立修饰符,单独使用

public class Shape {

public virtual void Draw()=>WriteLine($"Shape with {Position}and{size}")

}

- 也可以把属性声明为virtual。对于虚属性或重写属性,语法与非虚属性相同,但要在定义中添加关键字virtual,其语法如下

public virtual Size Size{get;set;} 可以给虚属性使用完整的属性语法: private Size _size; public virtual Size Size {

get
{
    return _size;
}
set
{
    _size=value;
}

}

- Size和Position类型重写了ToString()方法。这个方法在基类Object中声明virtual:

public class Position {

public int X{get;set;}
public int Y{get;set;}
public override string ToString()=>$"X:{X},Y:{Y}";

} public class Size {

public int Width{get;set;}
public int Height{get;set;}
public override string ToString()=>$"Width:{Width},Height:{Height}";

}


is和as运算符
- is和as是与继承有关的重要运算符

using 
- using关键字在C#中用多个用法,using声明用于导入名称空间。using语句处理实现IDisposable的对象,并在作用域的末尾用Dispose方法。

实现IDisposable接口和析构函数
- 利用运行库强制执行的析构函数,但析构函数的执行是不确定的,而且,由于垃圾回收器的工作方式,它会给运行库增加不可接受的系统开销。
- IDisposable 接口提供一种机制,该机制允许类的用户控制释放资源的时间,但需要确保调用Dispose()方法。
- 如果创建了终结器,就应该实现IDisposable接口。
- 双重实现的例子

using System; public class ResourceHolder:IDisposable {

private bool _isDisposed=false;
public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}
protect virtual void Dispose(bool disposing)
{
    if(!_isDisposed)
    {
        if(disposing)
        {

        }
    }
}
_isDisposed=true;

} -ResourceHolder() {

Dispose(false);

} public void SomeMethod() {

if(_isDisposed)
{
    throw new ObjectDisposedException("ResourceHolder");
}

} Dispose()方法有第二个protect的重载方法,它带一个布尔参数,这是真正完成清理工作的方法。Dispose(bool)方法由析构函数和IDisposable.Dispose()方法调用。 ```

IDisposable和终结器的规则

  • 如果类定义了实现IDisposable的成员,该类也应该实现IDisposable
  • 实现IDisposable并不意味着也应该实现一个终结器。终结器会带来额外的开销,因为它需要创建一个对象,释放该对象的内存,需要GC的额外处理。只在需要时才应该实现终结器,例如,发布本机资源。要释放本机资源,就需要终结器
  • 如果实现了终结器,也应该实现IDisposable接口。这样,本机资源可以早些释放,而不仅实在GC找出被占用的资源时,才释放资源。
  • 在终结器的实现代码中,不能访问已终结的对象。终结器的执行顺序是没有保证的
  • 如果所使用的一个对象实现了IDisposable接口,就不再需要对象时调用Dispose方法。如果在方法中使用这个对象,using语句比较方便。如果对象是类的一个成员,就让类也实现IDisPOSable。