一、c#支持重写实例方法和属性,但是不支持重写字段和任何静态成员,为了进行重写,需要将需要重写的成员用virtual关键字标记。
如下:
1)方法的重写
class Father
{
public virtual void MyActor()
{
Console.WriteLine("我是父亲");
}
}
class Me:Father
{
public override void MyActor()
{
Console.WriteLine("我是自己");
}
}
class Son:Me
{
public override void MyActor()
{
Console.WriteLine("我是儿子");
}
}
调用如下:
Father father = new Me();
father.MyActor();
调用的结果是:我是自己
所以我们可以看出当调用Father类的虚方法时,调用的就是实例化的子类的对象方法。
2)属性的重写
class MyFather
{
private int _Age;
public virtual int Age
{
get
{
if (_Age < 100 && _Age > 0)
{
return _Age;
}
else
{
return 0;
}
}
set
{
_Age = value;
}
}
}
class MyBrother:MyFather
{
public override int Age//语法糖
{
get => base.Age;//使用这个特殊的符号代替了括号和return
set => base.Age = value;
}
}
class My : MyFather
{
public override int Age
{
get
{
return base.Age;
}
set
{
base.Age=value ;
}
}
}
二、如果我们声明了虚方法,但是又不想重写该方法,但是程序中又报了警告,那么我们该怎么办?我们应该使用new关键字把父类的方法隐藏起来,等到真正调用的时候其实调用的还是父类声明的虚方法,如下:
class Father
{
public virtual void MyActor()
{
Console.WriteLine("我是父亲");
}
}
class Me:Father
{
public new void MyActor()
{
Console.WriteLine("我是自己");
}
}
class Son:Me
{
public new void MyActor()
{
Console.WriteLine("我是儿子");
}
}
调用如下:
Father father = new Son();
father.MyActor();
调用结果如下:
我是父亲
结果不是我们想象的他会调用Son类中的方法,他会调用父类的方法,说白了new操作符不能实现重写虚方法的功能,只能移除编译器警告。
三、Sealed修饰符
1)密封类中不能有virtual修饰的虚拟成员,否则会报错
2)密封类禁止从该类派生
3)密封成员无法在派生类中被重写
所以如果我们不想让一个类的成员被重写,就将这个成员用sealed修饰符修饰就好。
四、base成员
1)base的作用一个是在子类中调用父类的成员,如下:
class A
{
public virtual void Method()
{
}
}
class B:A
{
public override void Method()
{
Console.WriteLine("我是B类");
}
}
class C:B
{
public override void Method()
{
Console.WriteLine("我是C类");
}
}
class D:C
{
public override void Method()
{
base.Method();
}
}
调用如下:
A a = new D();
a.Method();
调用结果:
我是C类
说明base只是调用当前子类的父类的成员,但是不会一直调用到最上层的基类的成员。
2)在基类有很多个构造函数的时候。
class A
{
public string Name { get; set; }
public int Age { get; set; }
//public A()
//{
//}
public A(string name)
{
this.Name = name;
}
public A(string name, int age)
{
this.Name = name;
this.Age = age;
}
public virtual void Method()
{
}
}
class B : A
{
public B(string name):base(name)
{
}
public override void Method()
{
Console.WriteLine("我是B类");
}
}
class C : B
{
public C(string name) : base(name)
{
}
public override void Method()
{
Console.WriteLine("我是C类");
}
}
class D : C
{
public D(string name) : base(name)
{
}
public override void Method()
{
base.Method();
}
}
上面的代码中,分为三种情况:
1)基类没有构造函数,也就是有一个隐藏的无参的构造函数
那么子类不需要使用Base来调用基类的构造函数;
2)基类有一个无参的构造函数
子类不需要使用Base来调用基类的构造函数;
3)基类一个有参的构造函数
子类必须使用Base来调用这个无参的构造函数;
4)基类有多个有参的构造函数
子类至少要使用Base来调用基类的一个构造函数,否则
5)基类有多个有参的构造函数,同时有一个无参的构造函数
子类默认调用基类无参的构造函数。
五、抽象类
abstract class A
{
public string Name { get; set; }
public int Age { get; set; }
//public A()
//{
//}
public A(string name)
{
this.Name = name;
}
public A(string name, int age)
{
this.Name = name;
this.Age = age;
}
public virtual void VirtualMethod()
{
}
public abstract void Method();
}
class B : A
{
public B(string name):base(name)
{
}
public override void Method()
{
Console.WriteLine("我是B类的method");
}
public override void VirtualMethod()
{
Console.WriteLine("我是类B的virtualmethod");
}
}
如果我们就是想在父类中声明一些虚的成员(抽象成员),至于具体的实现都是在子类中,我们就可以将父类声明为抽象类(abstract修饰),然后在抽象类中声明抽象成员,这些抽象成员使用abstract修饰就好,或者virtual,这里说说一下virtual和abstract修饰的成员的区别:
1)virtual修饰的成员比如方法必须有实现,哪怕是只有一对大括号的空实现,但是abstract修饰的成员不允许有实现;
2)virtual修饰的成员可以被子类重写,也可以不被重写,程序不会报错,但是abstract修饰的成员一定要在子类中实现;
3)抽象类中virtual修饰的已经在父类中实现地成员没有意义,因为抽象类无法实例化。
六、所有的类都从object类派生,并且内置了诸如Equals、ToString、MemberwiseClone这些虚方法。
七、is操作符:
class A
{
public string Name { get; set; }
public int Age { get; set; }
//public A()
//{
//}
public A(string name)
{
this.Name = name;
}
public A(string name, int age)
{
this.Name = name;
this.Age = age;
}
public virtual void VirtualMethod()
{
}
}
class B : A
{
public B(string name):base(name)
{
}
public override void VirtualMethod()
{
Console.WriteLine("我是类B的virtualmethod");
}
}
调用如下:
A a = new A("");
B b = new B("");
if (a is object)
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
if (b is object)
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
if (b is A)
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
if (a is B)
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
结果:
true
true
true
false
说明通过is操作符可以判断子类对象是不是属于父类,而且所有类型都是属于object。
八、as操作符
A a = new A("");
B b = new B("");
A aa = b as A;
if(aa==null)
{
Console.WriteLine("转换失败");
}
else
{
Console.WriteLine("转换成功");
}
B bb = a as B;
if (bb == null)
{
Console.WriteLine("转换失败");
}
else
{
Console.WriteLine("转换成功");
}
结果:
转换成功
转换失败
上面代码说明子类可以直接转换为父类,但是父类不能直接转换为子类。
as操作符实现类型的转化,当转换失败的时候,就返回一个null,避免了抛出异常。