文章目录
1. virtual & abstract & interface 的区别
(1)virtual 修饰的方法
- virtual 关键字用于声明一个方法可以被其派生类重写(override)。如果一个基类中的方法被标记为 virtual,那么任何继承这个基类的派生类都可以选择覆盖(override)这个方法,提供不同的实现。
- 如果派生类没有显式地覆盖 virtual 方法,那么默认行为就是调用基类中的实现。
- virtual 方法必须有具体的实现,也就是说,你不能在声明 virtual 方法时省略方法体。
(2)abstract修饰的方法
- abstract 关键字用于声明一个类或方法是抽象的。抽象类不能被实例化,只能作为其他类的基类。
- 抽象方法只声明而不包含任何实现,它必须在派生类中被重写。
- abstract 方法所在的类也必须被声明为 abstract。
- 派生类可以选择性地覆盖 abstract 类中的非抽象方法,但是所有抽象方法都必须被实现,除非派生类本身也被声明为 abstract。
(3)interface修饰的方法
- interface 定义了一组方法、属性、事件和索引器的签名,但不包含任何实现细节。它们可以被多个类实现,从而允许这些类之间进行多态操作。
- 实现接口的类必须提供接口中声明的所有成员的具体实现。
- 接口可以被多个类实现,因此提供了多重继承的能力,因为 C# 不支持多重类继承。
总结:
virtual 提供了可选的重写能力,abstract 强制派生类必须实现某些功能,而 interface 则定义了一个完全由实现者填充的合同。
2. 一个class继承多个interface 的应用
step1: 创建2个接口文件



step2: 创建1个普通的class类文件:继承了2个interface

step3:实例化的tank 可以调用2个接口的方法

step4:最终效果:

step5:将实例tank赋值给不同的interface:将tank不同类型的功能进一步区分

step6:最终可以实现的类似下图的右半部分:

3. 一个class继承一个class和多个interface
如果Tank这个类还想继续扩展,需要添加一些tank自身属性的内容,就需要再多一些继承了
step1: 新建Myobjct类,设置属性的初始值:

step2: 初始化值的tank的时候,可以为tank赋值:

4. abstract作为中间介质(将不同的人以及不同的坦克关联到一起)

step1:新建项目TankProject,并新建Driver与Gunner类:
Driver 类仅仅负责driver相关的内容,具体是开什么车他都会开,就将IRunable对象传递进来即可(实例化的IRunable对象会定义具体的方法)


step2:Tank.cs文件的具体内容:
namespace Helloworld.AbstractStudy
{
// Tank 类仅做为abstract类,不具体定义,只负责定义哪些方法是必须要重写的
internal abstract class Tank : IFireable, IRunable
{
abstract public void Fire();
abstract public void FireAll();
abstract public void Run();
}
// 继承了Tank类,但因为Tank继承了2个interface,所以需要将这2个interface的所有方法重写一遍
class SuperTank: Tank
{
override public void Fire()
{
Console.WriteLine("SuperTank fire");
}
override public void FireAll()
{
Console.WriteLine("SuperTank fire all");
}
override public void Run()
{
Console.WriteLine("SuperTank run ...");
}
}
class HeavyTank : Tank
{
override public void Fire()
{
Console.WriteLine("HeavyTank fire");
}
override public void FireAll()
{
Console.WriteLine("HeavyTank fire all");
}
override public void Run()
{
Console.WriteLine("HeavyTank run ...");
}
}
}
step3:Program.cs文件的具体内容:
/*
角色:
1.Driver :IRunnable
2.Gunner :IFireable
3.Tank :
3.1 IFireable
3.2 IRunnable
(SuperTank + HeavyTank )
*/
using Helloworld.AbstractStudy;
namespace TankProject
{
class Progress
{
static void Main(string[] args)
{
Console.WriteLine("Hello, Welcome to my Tank Game!");
SuperTank st = new SuperTank();
//HeavyTank ht = new HeavyTank();
Driver driver = new Driver(st);
Gunner gunner = new Gunner(st);
gunner.Fire();
gunner.FireAll();
driver.Drive();
}
}
}
step4:最终显示效果:

5. set & get方法的使用
(1)class中的set方法的使用
- set 方法通常出现在属性(property)的定义中。属性提供了一种封装数据成员的方式,使代码更加清晰和易于维护。
- set 方法允许外部代码修改属性所代表的数据成员,而 get 方法则允许读取该数据成员的值。
public class Person
{
private string _name; // 私有字段,存储名字
// 定义一个名为 Name 的属性
public string Name
{
get { return _name; } // 返回名字
set { _name = value; } // 设置名字,value 是传递给 set 方法的参数
}
}
// 使用示例
class Program
{
static void Main(string[] args)
{
Person person = new Person();
// 调用 set 方法设置 Name 属性
person.Name = "John Doe";
// 调用 get 方法获取 Name 属性
Console.WriteLine(person.Name); // 输出: John Doe
}
}
(2)set & get 方法一起使用
namespace TankProject
{
internal class MainClass
{
static void Main(string[] args) {
Person person = new Person("alien");
Console.WriteLine(person.Name); // 输出: alien
// 调用 set 方法设置 Name 属性
person.Name = "Alien";
// 调用 get 方法获取 Name 属性
Console.WriteLine(person.Name); // 输出: Alien
}
public class Person
{
private string _name; // 私有字段,存储名字
public Person(string name)
{
_name = name;
}
// 定义一个名为 Name 的属性
public string Name
{
get { return _name; } // 返回名字
set { _name = value; } // 设置名字,value 是传递给 set 方法的参数
}
}
}
}


6. gunner & driver 作为两个消费者,如何消费只有单一功能(IFireable or IRunable的对象)
step1:创建2个新的类:Car & Rocket , 分别只继承IRunable、IFireable:


step2:为Driver & Rocket两个类分别设置set方法,方便修改传递进来的实例属性:


step3:为Driver & Rocket两个类分别设置set方法,方便修改传递进来的实例属性:

