28.Winfrom —— 窗体继承、泛型全套、协变逆变、预处理指令

目录

[一、窗体继承(虚属性 + 虚方法,多态实战)](#一、窗体继承(虚属性 + 虚方法,多态实战))

实现效果

[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我覆盖了父类的方法");
        }
    }
}

核心知识点

  1. virtual:标记属性 / 方法为可重写;
  2. override:子类重写父类虚成员;
  3. 多态:父类中调用ShowMessage(),运行时自动执行子类重写版本;
  4. 窗体继承复用界面控件与业务逻辑,快速开发同类功能窗口。

二、泛型基础(泛型方法、泛型类、泛型接口)

为什么使用泛型

普通重载方法只能固定类型;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();
        }
    }
}

核心概念

  1. 泛型方法 :方法名后<T>,方法内使用 T;
  2. 泛型类 :类名后<T>,整个类共享同一个 T;
  3. 泛型接口 :接口定义<T>,实现类必须指定类型;
  4. 优势:类型安全、无装箱拆箱、代码复用。

三、泛型内置委托 + 泛型约束 where

三种系统内置泛型委托

  1. Action<T>:无返回值,仅入参
  2. Func<T,TResult>:有返回值,支持多入参
  3. 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

  1. 关键字 out 修饰泛型参数;
  2. 转换方向:子类泛型 → 父类泛型(正向兼容);
  3. 仅用于接口 / 委托,T 只能作为返回值,不能作为入参;
  4. 内置:IEnumerable<out T>Func<in T,out TResult>IReadOnlyList<out T>

逆变 in T

  1. 关键字 in 修饰泛型参数;
  2. 转换方向:父类泛型 → 子类泛型(反向兼容);
  3. T 仅作为方法入参,不能作为返回值;
  4. 内置: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
    {
    }
}

五、预处理编译指令(编译阶段生效,运行不执行)

作用:代码编译前判断符号,选择性编译代码、抛出编译错误、警告、修改行号。 常用指令:

  1. #define / #undef:定义 / 取消编译符号
  2. #if #elif #else #endif:条件编译分支
  3. #error:编译直接报错,阻止生成程序
  4. #warning:编译提示警告,不阻断生成
  5. #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 两种环境都默认定义

全文知识点总结

  1. 窗体继承多态:virtual 虚成员 + override 重写,父类调用自动执行子类逻辑;
  2. 泛型:泛型方法 / 类 / 接口,解决类型复用与装箱性能问题,where 添加类型约束;
  3. 内置泛型委托:Action 无返回、Func 带返回、Predicate 布尔判断;
  4. 协变逆变 out 协变(子→父)、in 逆变(父→子),仅支持接口与委托;
  5. 预处理指令 编译期控制代码编译、抛出错误警告,区分 Debug/Release 环境