目录
[一、窗体继承(虚属性 + 虚方法,多态实战)](#一、窗体继承(虚属性 + 虚方法,多态实战))
[1. 父窗体 BaseForm.cs](#1. 父窗体 BaseForm.cs)
[2. 子窗体 ChildForm.cs(继承父窗体)](#2. 子窗体 ChildForm.cs(继承父窗体))
[三、泛型内置委托 + 泛型约束 where](#三、泛型内置委托 + 泛型约束 where)
[泛型约束 where](#泛型约束 where)
[四、协变 out、逆变 in 完整解析](#四、协变 out、逆变 in 完整解析)
[协变 out T](#协变 out T)
[逆变 in T](#逆变 in T)
一、窗体继承(虚属性 + 虚方法,多态实战)
实现效果
父基础窗体定义虚标题属性、虚弹窗方法;子客户管理窗体重写属性与方法。加载窗体自动显示子类标题,点击按钮弹出子类重写的提示框,典型面向对象多态。
1. 父窗体 BaseForm.cs
cs
using System;
using System.Windows.Forms;
namespace 窗体继承
{
public partial class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
}
// 虚属性:允许子类override重写
public virtual string Title
{
get { return "基础窗体"; }
}
// 虚方法:子类可重写覆盖逻辑
public virtual void ShowMessage()
{
MessageBox.Show("我是父类的方法");
}
// 按钮点击,调用虚方法(多态核心)
private void btnShow_Click(object sender, EventArgs e)
{
ShowMessage();
}
// 窗体加载,读取虚属性赋值标题
private void BaseForm_Load(object sender, EventArgs e)
{
this.Text = Title;
lblTitle.Text = "当前窗体:" + Title;
}
}
}
2. 子窗体 ChildForm.cs(继承父窗体)
cs
using System;
using System.Windows.Forms;
namespace 窗体继承
{
// 继承BaseForm,自动拥有父窗体所有控件、方法、属性
public partial class ChildForm : BaseForm
{
public ChildForm()
{
InitializeComponent();
}
// 重写父类虚属性
public override string Title
{
get { return "子类窗体 - 客户管理"; }
}
// 重写父类弹窗方法,覆盖原有逻辑
public override void ShowMessage()
{
MessageBox.Show("我是子类重写的方法!\n我覆盖了父类的方法");
}
}
}
核心知识点
virtual:标记属性 / 方法为可重写;override:子类重写父类虚成员;- 多态:父类中调用
ShowMessage(),运行时自动执行子类重写版本; - 窗体继承复用界面控件与业务逻辑,快速开发同类功能窗口。
二、泛型基础(泛型方法、泛型类、泛型接口)
为什么使用泛型
普通重载方法只能固定类型;object 传参存在装箱拆箱损耗;泛型 T 代表任意类型,编译时确定真实类型,无装箱、类型安全、一套代码适配所有数据类型。
cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace _1泛型基础
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// 普通重载方法,每种类型写一套
F1(10);
F2("张三");
// object方案(不推荐,装箱拆箱)
F3(DateTime.Now);
// 泛型方法,一个方法适配所有类型
F4(10);
F4<string>("zs");
// 带泛型参数返回值
Console.WriteLine(Sum(10,20));
Console.WriteLine(Sum1(10, 20.2));
// 1.泛型类传入基础类型
People<string> people = new People<string>();
people.Langue = "字符串类型";
// 2.泛型类传入自定义实体
People<China> p1 = new People<China>();
p1.Langue = new China();
// 3.泛型构造函数
People<Japan> p2 = new People<Japan>(new Japan());
Console.WriteLine(p2.Every.Langue);
// 泛型接口实现类
Dog<int> d1 = new Dog<int>();
}
// 固定int重载
public void F1(int a)
{
Console.WriteLine($"参数的类型{a.GetType().FullName},参数值为:{a}");
}
// 固定string重载
public void F2(string a)
{
Console.WriteLine($"参数的类型{a.GetType().FullName},参数值为:{a}");
}
// object方案,存在装箱拆箱性能损耗
public void F3(object a)
{
Console.WriteLine($"参数的类型{a.GetType().FullName},参数值为:{a}");
}
// 泛型方法 T代表任意未知类型,调用时确定
public void F4<T>(T a )
{
Console.WriteLine($"参数的类型{a.GetType().FullName},参数值为:{a}");
}
// 单类型泛型返回方法
public T Sum<T>(T a,T b)
{
dynamic res = a;
res += b;
return res;
}
// 双不同类型泛型方法
public K Sum1<T,K>(T a, K b)
{
dynamic res = a;
res += b;
return res;
}
}
// 自定义实体1
public class China
{
public string Langue { get; set; } = "汉语";
public void Eat( string content)
{
Console.WriteLine(content);
}
}
// 自定义实体2
public class Japan
{
public string Langue { get; set; } = "日语";
public void Eat(string content)
{
Console.WriteLine(content);
}
}
// 泛型类:类名后<T>,类内所有T统一类型
public class People<T>
{
public T Langue { get; set; }
// 普通方法,使用类泛型T,不是泛型方法
public void Eat(T content)
{
Console.WriteLine(content);
}
// 无参构造
public People()
{
}
public T Every { get; set; }
// 泛型构造函数
public People(T e)
{
Every = e;
}
}
// 泛型接口
public interface IAnimal<T>
{
T Sum(T a, T b);
}
// 实现泛型接口
public class Dog<T> : IAnimal<T>
{
public T Sum(T a, T b)
{
throw new NotImplementedException();
}
}
}
核心概念
- 泛型方法 :方法名后
<T>,方法内使用 T; - 泛型类 :类名后
<T>,整个类共享同一个 T; - 泛型接口 :接口定义
<T>,实现类必须指定类型; - 优势:类型安全、无装箱拆箱、代码复用。
三、泛型内置委托 + 泛型约束 where
三种系统内置泛型委托
Action<T>:无返回值,仅入参Func<T,TResult>:有返回值,支持多入参Predicate<T>:固定返回 bool,常用于条件筛选
泛型约束 where
where T : class:T 必须是引用类型(类、接口)where T : struct:T 必须是值类型where T : 父类:T 必须继承该父类where T : new():T 必须有无参构造函数
cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace _2泛型委托
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Action 无返回泛型委托
Action<int> a1 = a => { Console.WriteLine("无返回值的委托方法"); };
a1(10);
// Func 带返回泛型委托
Func<int, int> a2 = a => a * 10;
MessageBox.Show(a2(10)+"");
// Predicate 返回bool,判断条件
Predicate<int> a3 = a => a % 2 == 0;
MessageBox.Show(a3(10) + "");
// class约束:只能传引用类型
F1("");
F1(new int[] {});
// 父类约束:只能传People及其子类
F2(new People());
F2(new Student());
}
// where T : class 约束:T必须为引用类型
public void F1<T>(T a) where T : class
{
}
// where T : People 基类约束:只能传People/Student
public void F2<T>(T a) where T : People
{
}
}
public class People
{
}
public class Student: People
{
}
}
四、协变 out、逆变 in 完整解析
基础父子类转换
子类对象可以隐式赋值给父类变量;父类对象必须强制转换才能赋值给子类变量。 但泛型集合List<父>、List<子>不存在继承关系,直接赋值报错,需要协变 / 逆变接口IEnumerable<out T>、Action<in T>
协变 out T
- 关键字
out修饰泛型参数; - 转换方向:子类泛型 → 父类泛型(正向兼容);
- 仅用于接口 / 委托,T 只能作为返回值,不能作为入参;
- 内置:
IEnumerable<out T>、Func<in T,out TResult>、IReadOnlyList<out T>
逆变 in T
- 关键字
in修饰泛型参数; - 转换方向:父类泛型 → 子类泛型(反向兼容);
- T 仅作为方法入参,不能作为返回值;
- 内置:
Action<in T>、Predicate<in T>、IComparer<in T>
cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace _3逆变和协变
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// 普通父子类转换
Bird bird = new Bird();
Sparrow sp = new Sparrow();
bird = sp; // 子类隐式转父类
sp = (Sparrow)bird; // 父类强制转子类
// List<T>无协变,直接赋值报错
List<Bird> birds = new List<Bird>();
List<Sparrow> sparrows = new List<Sparrow>();
// birds = sparrows; 报错
// IEnumerable<out T> 协变,允许子类集合赋值给父类接口变量
IEnumerable<Bird> birds1 = new List<Sparrow>();
// Func 输出out协变:Func<子类返回值> 赋值给 Func<父类返回值>
Func<int, Sparrow> f1 = a => new Sparrow();
Func<int, Bird> f2 = f1;
// Predicate<in T> 逆变:父类委托赋值给子类委托
Predicate<Bird> f3 = a=> true;
Predicate<Sparrow> f4 = f3;
// Action<in T> 逆变
Action<object> f5 = a => { };
Action<Bird> f6 = f5;
// Func入参in逆变
Func<Bird, int> f7 = a => 10;
Func<Sparrow, int> f8 = f7;
}
}
public class Bird
{
}
public class Sparrow:Bird
{
}
}
五、预处理编译指令(编译阶段生效,运行不执行)
作用:代码编译前判断符号,选择性编译代码、抛出编译错误、警告、修改行号。 常用指令:
#define/#undef:定义 / 取消编译符号#if #elif #else #endif:条件编译分支#error:编译直接报错,阻止生成程序#warning:编译提示警告,不阻断生成#line:修改报错行号、文件名
cs
#define Debug
#undef Debug
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace _4预处理指令
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// 条件编译分支
#if Debug
Console.WriteLine("111");
#elif DEBUG
Console.WriteLine("222");
#else
Console.WriteLine("3333");
#endif
// 无DEBUG符号直接编译报错
#if !DEBUG
#error 该代码不允许在Release模式下编译
Console.WriteLine("测试");
#endif
// 编译警告提示
#warning 该方法包含过时API,建议替换
void Test()
{
}
Test();
// 自定义报错行号与文件
#line 46 "Form1.cs"
int a = 10;
Console.WriteLine(a / 0);
#line default
}
}
}
系统内置编译符号
DEBUG:调试模式自动定义TRACE:Debug/Release 两种环境都默认定义
全文知识点总结
- 窗体继承多态:virtual 虚成员 + override 重写,父类调用自动执行子类逻辑;
- 泛型:泛型方法 / 类 / 接口,解决类型复用与装箱性能问题,where 添加类型约束;
- 内置泛型委托:Action 无返回、Func 带返回、Predicate 布尔判断;
- 协变逆变 out 协变(子→父)、in 逆变(父→子),仅支持接口与委托;
- 预处理指令 编译期控制代码编译、抛出错误警告,区分 Debug/Release 环境