一、. 接口 interface ------ C++ 纯虚基类的「升级版」
C++ 里你想要「只定规矩、不定实现」会用纯虚基类:
// C++:纯虚基类 = 接口
class Ishape
{
public:
virtual ~Ishape() = default; //c++习惯:虚析构
virtual double Area() const= 0; // 纯虚函数 = 0
virtual void Print() const= 0;
}
//c#interface 自带【抽象】属性,不用写virtual 、不用=0
Interface Ishape
{
public:
double Area(); //默认抽象,分号结尾
void Print();
}
1.1 C++ vs C# 对照表
|----------|--------------------------|----------------------|
| 维度 | C++ 纯虚基类 | C# interface |
| 关键字 | class + = 0 | interface(独立关键字) |
| 成员默认 | 普通函数,需手写 virtual ... = 0 | 自动抽象,不写 virtual |
| 访问修饰 | 自己写 public: | 一律 public,不能写 public |
| 字段(数据成员) | 可以有 | 不能有(只能有方法/属性/事件) |
| 多继承 | 可以,但容易钻石继承 | 类可实现多个接口,天然解冲突 |
| 虚析构 | 必须手写 virtual ~Foo() | 不需要(C# 有 GC,没这坑) |
1.2 三个关键规则(C# 特有,C++ 没有的约定)
1、接口名约定用 I 开头:IShape、IDrawable、IComparable。这是 .NET 的硬约定,C++ 没这规矩。
2、接口里不能写字段,但可以写「属性」------属性本质是方法嘛:
interface IAnimal
{
string Name { get; set; } // ✅ 属性,本质是两个方法 get_/set_
// int age; // ❌ 编译错误,接口不能有字段
}
3、类可以实现多个接口(这是 C# 比 C++ 多继承更优雅的地方,后面练习会演示)。
二、继承语法 : ------ C# 用一个冒号通吃
C++ 继承要写访问修饰:class Dog : public Animal。
C# 不用,一个冒号搞定,因为 C# 只有 public 继承(没有 private/protected 继承那套)。
//c++
class Cricle : public Ishape{};
//c# --冒号后面直接跟基类/接口
class Cricle : Ishape{} //实现接口
class Dog : Animal{} //继承类
class Bagle :Animal,IFlyable,IComparable{} //一个基类 + 多个接口
⚠️ 规则:基类只能有一个(C# 不允许多继承类),接口可以有任意多个。基类要写在最前面。
调用基类构造:base(...) ≈ C++ 的 Base(...)
// C++:初始化列表调基类构造
class Dog : public Animal {
public:
Dog(string n) : Animal(string n) //基类构造
};
// C#:base(...) 相当于 C++ 初始化列表
// C++:初始化列表调基类构造
class Dog : Animal {
public Dog(string n) : base(string n) //调用animal的构造函数
};
三、 is / as ------ C# 版的 dynamic_cast
这是 C++ 转 C# 最容易对应上的点。C++ 你用 dynamic_cast 做运行时类型转换:
// C++:dynamic_cast
IShape* s = new Circle(2);
Circle* c = dynamic_cast<Circle*>(s); // 失败返回nullptr
if(dynamic_cast<Rectangle*>(s)) // 判断是不是某个类型
{
}
C# 拆成两个更直观的关键字:
|-----------------------------------|--------|--------------------------|
| C++ | C# | 干什么 |
| dynamic_cast<T*>(p) != nullptr | p is T | 判断:是不是这个类型,返回 bool |
| dynamic_cast<T*>(p) | p as T | 转换:能转就给对象,不能转给 null(不抛异常 |
// C++ as
Ishape s = new Cricle(2);
if(s is Rectangle){} //false
Circle c = s as Cricle; // 成功->Circle对象
Rectangle r = s as Rectangle; // 失败 → null,不崩
小提示:C# 7+ 还有个更顺手的 if (s is Circle c) ------判断+转换+变量名一步到位,后面练习里我会用到。
using System;
using System.Collections.Generic;
interface IShapes
{
double Area();
void Print();
}
interface IDrawable
{
void Draw();
}
class Circle : IShapes, IDrawable // 冒号 + 接口列表,一个类实现多个接口
{
public double Radius { get; }
public Circle(double radius)
{
Radius = radius;
}
public double Area() => 3.14 * Radius * Radius;
public void Print() => Console.WriteLine($"圆的半径 ={Radius}面积={Area():F2}");
public void Draw() => Console.WriteLine($" 画一个圆 ○ (半径 {Radius})");
}
class Rectangle : IShapes // 只实现 IShape
{
public double width { get; }
public double Length { get; }
public Rectangle(double w, double h)
{
width = w;
Length = h;
}
public double Area() => width * Length;
public void Print() => Console.WriteLine($"矩形长宽 ={width}x{Length} 面积={Area():F2}");
}
class Animal
{
public string Name { get; }
public Animal(string name) => Name = name;
public virtual void Speak() => Console.WriteLine($"{Name}: ...");
}
class Dog : Animal
{
public Dog(string name) : base(name) { } // base(...) ≈ C++ 初始化列表
public override void Speak()=> Console.WriteLine($"{Name}: DOGDOG");
}
class Program
{
static void Main()
{
// 用接口类型装具体对象 ------ 这就是多态的雏形
// C++ 等价:vector<IShape*> shapes;
List<IShapes> shapes = new()
{
new Circle(3),
new Rectangle(4, 5),
new Circle(1),
new Rectangle(2, 3)
};
Console.WriteLine("===== 所有图形 =====");
double total = 0;
foreach(var s in shapes)
{
s.Print();
total += s.Area();
}
Console.WriteLine($"面积={total:F2}");
// is / as:把圆单独挑出来(≈ C++ dynamic_cast)
Console.WriteLine("===== 挑出圆,打印半径 =====");
foreach(var s in shapes)
{
if(s is Circle c)
{
Console.WriteLine($"招到圆,圆的半径={c.Radius}");
c.Draw(); // 圆实现了 IDrawable,可以调
}
}
//as 用法
Console.WriteLine();
IShapes first = shapes[0];
Rectangle r = first as Rectangle;
// 继承 demo
Console.WriteLine("\n===== 继承 demo =====");
Animal a = new Dog("wangcai");
a.Speak();
}
}