继承
- 一个类可以继承另一个类,从而对原有类进行扩 展和自定义
- 可以叫做子类(派生类)和父类(基类)
- 继承的类让你可以重用被继承类的功能
- C#里,一个类只能继承于一个类,但是这个类却可以被多个类继承
class classGrandpa
{
public string PublicGrandpaString = "classGrandpa!";
}
class classParent : classGrandpa
{
private int PrivateParentInt = 0;
public string PublicParentString = "classParent!";
public void SayHello() { }
}
class classChild : classParent
{
void Method()
{
//System.Console.WriteLine(PrivateParentInt); //Error:PrivateParentInt不可访问
Console.WriteLine(PublicParentString);//print:classParent!
Console.WriteLine(PublicGrandpaString);
}
static void Main() => new classChild().Method();
}
多态
引用是多态的,类型为x的变量可以引用其子类的对象
根据继承的例子:
class test
{
public void Display(classGrandpa grandpa) { }
public void Main()
{
Display(new classParent());
Display(new classChild());
}
}
因为子类具有父类的全部功能特性,所以参数可以使子类
但是反过来就不行了
因为子类可能有一些属性,父类没有
引用转换
- 一个对象的引用可以隐式的转换到其父类的引用(向上转换)
- 想转换到子类的引用则需要显示转换(向下转换)
引用转换原理:创建了一个新的引用,它也指向同一个对象
- 向上转换
- 从子类的引用创建父类的引用
classChild child = new classChild();
classParent parent = child; // 向上转换
- 从子类的引用创建父类的引用
- 向上转换
变量parent 依然指向同一个classChild 对象(child 也指向它)
Console.WriteLine(child == parent); // True
尽管子类的引用child和parent指向同一个对象,但是parent的可视范围更小一些
Console.WriteLine(parent.PublicParentString); // True
Console.WriteLine(parent.PublicChildInt); // Error :没有包含此定义
向下转换
classChild child = new classChild();
classParent parent = child; // 向上转换
classChild child1 = (classChild)parent; //向下转换
Console.WriteLine(child1.PublicChildInt); // No Error
Console.WriteLine(child1 == parent); //True
Console.WriteLine(child1 == child); //True
和向上转换一样,只涉及到引用,底层的对象不会受影响
- 需要显式转换,因为可能会失败
- 如果向下转换失败,那么会抛出InvalidCaseException(属于运行时类型检查)
AS操作符
as操作符会执行向下转换,如果转换失败,不会抛出异常,值会变为null
classGrandpa grandpa = new classGrandpa();
classChild child = grandpa as classChild; //失败也不会抛出异常
as操作符无法做自定义转换
long x = 3 as long; // 不可以
IS操作符
is操作符会检验引用的转换是否成功。换句话说,判断对象是否派生于某个类(或者实现了某个接口)
通常用下向下转换前的验证:
if(parent is classChild)
{
Console.WriteLine(((classChild)parent).xxx)
}
如果拆箱可以转换成功的话,那么使用is操作符的结果会是true
模式变量(C#7,在使用is操作符的时候,可以引入一个变量)
VIRTUAL函数成员(虚)
标记为virtual的函数可以被子类重写,包括方法、属性、索引器、事件
public class Asset
{
public string Name;
public virtual decimal liability => 0;
}
OVERRIDE重写
- 使用override修饰符,子类可以重写父类的函数
public class Asset
{
public string Name;
public virtual decimal liability => 0;
}
public class Stock : Asset
{
public long SharesOwned;
}
public class House : Asset
{
public decimal Mortgage;
public override decimal liability => Mortgage;
void Display()
{
House mansion = new House { Name = "McManstion", Mortgage = 250000 };
Asset a = mansion;
System.Console.WriteLine(mansion.liability); //250000
System.Console.WriteLine(a.liability); //250000
}
static void Main()
{
new House().Display();
}
}
- virtual方法和重写方法的签名、返回类型、可访问程度必须是一样的
- 重写方法里使用base关键字可以调用父类的实现
- 注意
- 在构造函数里调用virtual方法可能比较危险,因为编写子类的开发成员可能不知道他们在重写方法的时候,面对的是一个未完全初始化的对象。
- 换句话说,重写的方法可能会访问依赖于还未被构造函数初始化的字段的属性或方法
class Parent
{
protected readonly string name;
public Parent()
{
GetNameLength();
name = "MJ";
}
public virtual int GetNameLength()
{
return 0;
}
}
class Child : Parent
{
public override int GetNameLength()
{
return base.name.Length; //编译器并不会直接报错,Error
}
}