事件
事件的模型有五个组成部分:事件拥有者、事件触发、事件响应者、事件处理器、事件订阅
示例代码:最基本的情况
using System;
using System.Timers;
class Program
{
static void Main(string[] args)
{
System.Timers.Timer timer = new System.Timers.Timer(); //事件拥有者
Boy boy = new Boy(); //事件响应者
Grily grily = new Grily();
timer.Interval = 1000; //Interval 时间间隔
timer.Elapsed += boy.Action;
timer.Elapsed += grily.Action;
// boy.Action是事件处理器
//其中事件订阅是 这个+=操作符
timer.Start();
Console.ReadLine();
}
}
class Boy
{
internal void Action(object? sender, ElapsedEventArgs e)
//他上面这句object? sender, ElapsedEventArgs e 是用来判断对象是否用于事件订阅如果有则进行事件处理
//事件处理器
{
Console.WriteLine("领域展开");
}
}
class Grily
{
internal void Action(object? sender, ElapsedEventArgs e)
{
Console.WriteLine("无量空处");
}
}
小总结:其中 Boy和Grily受到事件响应所触发,timer是事件拥有者本身、通过 +=来去对事件进行订阅
第一种情况
通过事件的响应者去订阅事件的拥有者:事件响应=>去找到这个响应是那个事件去进行的
namespace chuangti
{
class Program
{
static void Main(string[] args)
{
Form form = new Form(); //事件的拥有者
Constal constal = new Constal(form); //事件的响应者
form.ShowDialog();
}
}
class Constal
{
private Form form;
public Constal(Form form)
{
if (form != null)
{
this.form = form; //这里this所指向的form 是自己定义private 成员变量的form
this.form.Click += this.FormConstall;
}
}
private void FormConstall(object? sender, EventArgs e)
{
this.form.Text = DateTime.Now.ToString();
}
}
}
第二种情况
这个对象是事件的拥有者的同时也是事件的响应者
using System;
using System.Windows.Forms;
namespace chuangti
{
class Program
{
static void Main(string[] args)
{
//form是事件的拥有者同时也是事件的响应者
MyForm form = new MyForm();
form.Click += form.FormClick;
form.ShowDialog();
}
}
class MyForm : Form
{
internal void FormClick(object? sender, EventArgs e)
{
this.Text = DateTime.Now.ToString();
}
}
}
总结:
在 Main 方法中,创建了一个名为 form 的 MyForm 对象,MyForm 是继承自 Form 的自定义窗体类。这个窗体既是事件的拥有者,也是事件的响应者。
定义了一个名为 MyForm 的类,它继承自 Form 类。在 MyForm 类中,定义了一个名为 FormClick 的方法,该方法是用来处理窗体点击事件的。
第三种也是最常用的
事件的响应者是事件拥有者的一个字段,通过方法去订阅自身成员的某个对象/字段
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.button1.Click += BuronClil;
}
private void BuronClil(object? sender, EventArgs e)
{
this.textBox1.Text = "现在是我的事件";
}
}
//其实很简单,一个比较基础的逻辑
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.button1.Click += BuronClil;
}
private void BuronClil(object? sender, EventArgs e)
{
//sender是指事件拥有者 EventArgs e 是事件的响应者
if(sender == this.button1)
{
this.textBox1.Text = "点击的第一个按钮";
}
}
事件的完整流程
例子是这样的:顾客来到吃饭的地方,然后小儿过来给顾客进行点菜,小儿这个人只为点菜而生,然后顾客吃完之后进行结账
事件:点菜
成员:事件的拥有者、事件的响应者。 小儿只为点菜,所以是事件的拥有者、顾客因为小儿过来,所以才进行点菜,这是事件的响应者。
`事件是基于委托的` :就是事件的返回值参数类型是与委托所定义的参数类型是一致的。 而且事件的响应者提供给事件拥有者适合的处理,需要找一个地方对这个处理进行一个存储
上代码:
using System;
namespace shijain
{
class Program
{
static void Main(string[] args)
{
Custorm custorm = new Custorm(); //事件的拥有者
Watier watier = new Watier(); //事件的响应者
custorm.Order += watier.Action; //进行一个事件的订阅 Action是自定义方法名称 //事件的处理器 事件的订阅
//现在事件5部分已经有就四个,现在就缺少事件的触发
custorm.Action();
custorm.PlayBill();
}
}
//小二点餐的事件
public class OrderEventArgs : EventArgs
{
public string BillName { get; set; }
public string BillSize { get; set; }
}
//现在事件的拥有者和事件的响应者都有了,那么就需要创建事件,但是事件通常都是带上委托的
//所以创建事件之前必须要自定义一个委托进行对事件的类型限制
public delegate void OrderEventHandler(Custorm custorm, OrderEventArgs e);
//点餐得到的总价格
public class Custorm
{
//委托创建好了,就要创建一个委托的类型字段,用来引用和处理事件的
private OrderEventHandler orderEventHandler;
//创建事件,事件的类型就是上方定义的类型字段
public event OrderEventHandler Order; //其中event就是创建事件的关键字
public double Bill { set; get; }
public void PlayBill()
{
Console.WriteLine("这顿饭一共多少钱:" + Bill);
}
//事件的触发
public void Think()
{
for(int i = 0; i<=5; i++)
{
Console.WriteLine("让我想想");
Thread.Sleep(1000);
}
if (this.Order != null)
{
OrderEventArgs e = new OrderEventArgs();
e.BillName = "干炒牛河";
e.BillSize = "large";
this.Order(this, e);
}
}
public void Action()
{
Console.ReadLine();
this.Think();
}
}
//创建一个事件的响应者
class Watier
{
public void Action(Custorm custorm, OrderEventArgs e)
{
Console.WriteLine("我想吃:" + e.BillName + "" + e.BillSize);
double price = 10;
switch (e.BillSize)
{
case "small":
price = price * 0.5;
break;
case "large":
price = price * 1.5;
break;
default:
break;
}
custorm.Bill += price;
}
}
}
总结:
1、从上方的代码我们知道,在创建事件之前,都会创建一个委托和一个委托字段,用来去当作事件的类型限制,所以创建事件之前,必须跟上委托。
2、事件的拥有者和事件的订阅者,也没什么好说的,创建实例之后通过+=来去对事件进行订阅
3、通过+= 订阅的方法:+= watier.Action Action就是充当事件的处理器
4、 OrderEventArgs : EventArgs 当触发时候需要传递一些额外的参数给事件处理器的时候,通常都会去继承EventArgs 虽然EventArgs可有可无,但是可以作为一个编程习惯去进行
5、关于事件:事件在定义的时候通常都会有add 和remove这2个方法 但是如果我们手动去进行方法创建,那么到时候访问的是我们手动的创建的那个名称 不是方法名称 看代码
public event OrderEventHandler Order
{
add{ this.orderEventHandler += value}
remove{this.orderEventHnadler = value}
}
//那么这个时候如果要去访问这个方法 那么访问的格式是
this.orderEventHandler //而不是访问Order
public event OrderEventHandler Order;
//当我们通过这种方法去进行简写 那么访问的时候是直接访问方法名称
this.Order
//一个语法糖
6、为什么有了委托字段/熟悉之后,还要需要事件:这是为了让程序更加的有逻辑性。其实事件的本身就是委托字段的一个包装器
改写和多态
总结: 反正多态:就是看你实例化的是那一部分你所调用是看那一部分 但是问题来了,如果只是简单的重写那么 在子类和父类之间是不需要去通过 virtual override关键字去进行改写 但是如果进行多态操作,那么就需要在拥有这2个关键字(虚方法)的前提下才能执行,不然实例化的东西无效
namespace Pver
{
class Program
{
static void Main(string[] args)
{
Vehi v = new Car();
v.Run(); //Car Running---
Vehi a = new Vehi();
Car b = new Car();
a.Run(); //Running————
b.Run(); //Car Running---
}
}
class Vehi
{
public virtual void Run()
{
Console.WriteLine("Running————");
}
}
class Car : Vehi
{
public override void Run()
{
Console.WriteLine("Car Running---");
}
}
}
抽象类
1、抽象类是一个没有方法体的方法,主要是用来被其他东西继承然后进行改写的一种桥梁
2、看下方代码,Run方法是需要进行一个重写的,那么为什么我们不直接把Vehicle 中的run方法的方法体清空,去置空一个抽象方法,然后等后续添加类的时候,直接去进行对run方法的重写
3、抽象类是无法进行实例化的,反正抽象类/抽象方法,就是一个给别人当父类的一种东西
namespace go
{
class Program
{
static void Main(string[] args)
{
Vehicle v = new Car();
v.run();
v.stop();
// Vehicle v = new Vehicle(); 无法实现
}
}
abstract class Vehicle
{
public void stop()
{
Console.WriteLine("Stopping");
}
//public virtual void run()
//{
// Console.WriteLine("Vehicle Running");
//}
public abstract void run();
}
class Car : Vehicle
{
public override void run()
{
Console.WriteLine("Car running");
}
}
class NewCar:Vehicle
{
//当我们去进行继承的时候,编译器已经直接提示我们实现这个抽象类方法了
public override void run()
{
Console.WriteLine("使用抽象类实现的");
}
}
接口与单元测试
接口是抽象类进化而来的,接口中的抽象方法,必须都是public
总结:接口它相当于就是一种契约,它定义了一组方法、属性和事件,但不提供实现细节。接口提供了一种规范,用于指定类应该具备什么样的行为或功能,通过关键字interface去声明,接口内容包含方法、属性、事件和索引器的声明。
接口代码案例:
class Program
{
static void Main(string[] args)
{
var user = new PhoneUser(new Nokia());
//至于为什么使用var来去声明对象,那是因为var可以是任意类型
user.UserPhone();
}
}
class PhoneUser
{
//这个就是一个构造器
private Iphone _phone;
public PhoneUser(Iphone iphone) {
_phone = iphone;
}
public void UserPhone()
{
_phone.Dail();
_phone.Receive();
_phone.Pick();
_phone.Send();
}
}
interface Iphone
//通过接口定义了几个还为实现的方法
{
void Dail();
void Pick();
void Send();
void Receive();
}
class Nokia : Iphone
//然后通过继承来去对接口内的方法去进行一个重写,相当于抽象类
{
public void Dail()
{
Console.WriteLine("Calling...");
}
public void Pick()
{
Console.WriteLine("Hello Time...");
}
public void Receive()
{
Console.WriteLine("Message...");
}
public void Send()
{
Console.WriteLine("Hello...");
}
}
接口隔离
接口隔离原则:一个比较理论的东西,每个接口应该只包含客户端所需的方法,避免定义过于庞大的、臃肿的接口。就是宁愿分多几个接口去处理,都不要让一个接口去处理所有的事情
class Program
{
static void Main(string[] args)
{
//var dit = new Drive(new Car());
var dit = new Drive(new Tank()); //调用的是Tank继承接口的Run方法
//至于为什么Tank也可以使用,那是因为我们Truck接口继承的是CarM的接口
//Tank实现Truck接口中的Run其实就是实现CarM接口里面的Run方法
dit.Driver();
}
}
class Drive
{
private CarM a1;
public Drive(CarM a2) {a1 = a2;}
public void Driver(){a1.Run(); }
}
interface CarM{void Run();}
class Car : CarM
{
public void Run()
{
Console.WriteLine("现在汽车开始奔跑");
}
}
interface Truck :CarM,a1
{
//通过a1这个接口把Fire这个方法分出去了,剩下Run这个方法,因为跟我们上面CarM接口的Run方法差不多所以直接继承过来重写一下就好了
//接口的多个继承
//void Fire();
//void Run();
}
//现在通过接口隔离原则去处理Truck这个接口
interface a1{void Fire();}
class Tank : Truck
{
public void Fire()
{
Console.WriteLine("坦克开火!");
}
public void Run()
{
Console.WriteLine("现在是继承过来然后进行重写的,坦克快跑!!!");
}
}
接口隔离的第二种情况
using Microsoft.VisualBasic;
using System;
using System.Collections;
namespace dsio
{
class Program
{
static void Main(string[] args)
{
var Gite = new WarmKiller();
Gite.love();
//Gite.kill();
//这种是正常的接口隔离写法,但是这种写法是任何只要是WarmKiller实例化的变量都可以进行调用接口方法
//但是另外一种情况是,必须要是调用某一个指定变量才会看到其中的方法,相当于多一层保密性
//因为我下面给kill这个方法加上的一个保密,所以如果没有对应的条件是无法直接调用kill这个方法的
Killer wx = new WarmKiller();
wx.kill();
//通过拥有Killer这个类型才能使用kill这个方法
//如果想要wx 也可以使用love这个方法,可以通过类型转换去实现
var man = (WarmKiller)wx;
man.love();
}
}
interface Killer {void kill();}
interface Maner { void love(); }
class WarmKiller : Maner,Killer
{
public void love()
{
Console.WriteLine("I will love for every one");
}
//public void kill()
//{
// Console.WriteLine("I will kill for every man and womeram");
//}
//上面这种是普通的写法
void Killer.kill()
{
Console.WriteLine("必须要又Killer这个变量才可以使用这个方法");
}
}
}
反射
反射是指在运行时动态地获取、检查和操作程序的类型、成员(如字段、属性、方法)以及调用方法等。它使我们能够在编译时未知类型的情况下,通过元数据信息对代码进行操作和探索。
反射的基本原理可以分为以下几个步骤:
1、首先要去获取类型信息: 通过GetType()去获取需要进行操作的类型的元信息
2、拿到了信息之后,保险起见可以通过一个对象去对这些信息进行一个保存 object
3、创建实例或调用方法:通过Activator.CreateInstance()方法根据类型对象创建实例
4、使用MethodInfo.Invoke() 方法可以调用方法。
5、操作字段和属性:通过 FieldInfo.GetValue() 和 FieldInfo.SetValue() 方法可以读取和设置字段的值,通过 PropertyInfo.GetValue() 和 PropertyInfo.SetValue() 方法可以读取和设置属性的值。
using System.Reflection;
namespace fanshe
{
class Program
{
static void Main(string[] args)
{
ITank tank = new HevveTank();
//通过GetType获取到tank对象的信息
var t = tank.GetType();
object o = Activator.CreateInstance(t);
//Activator.CreateInstance 是一个反射相关的静态方法,用于在运行时动态创建对象实例
//根据给定的类型信息创建该类型的对象,并返回这个对象
MethodInfo fireMi = t.GetMethod("Fire");
MethodInfo runMi = t.GetMethod("Run");
// MethodInfo 是一个表示方法数据信息的类,通过它可以获取到方法的名称、参数、返回值等
fireMi.Invoke(o, null);
runMi.Invoke(o, null);
//对于反射的调用,它的第一个参数是反射的对象,第二个是是否要给予参数,如果没有那么就是null
}
}
interface ITank
{
void Run();
void Fire();
}
class HevveTank:ITank
{
public void Run()
{
Console.WriteLine("Running....");
}
public void Fire()
{
Console.WriteLine("Boom.....");
}
}
}
依赖注入
依赖反转: 依赖反转就是,将服务方提供的服务,使用方使用的前提条件都分别抽象到两个不同的接口里,从本来使用者到服务者由上而下的依赖关系,转化为分属于两个接口的具体实现
依赖注入它是依赖.net中 下方图片中的框架
在依赖注入中,有三个主要角色:
- 依赖:组件所需的外部对象或服务,可以是其他类、接口、配置数据等。
- 客户端:需要使用依赖的组件或类。
- 注入器/容器:负责创建和管理依赖关系的对象。它会在客户端需要依赖时将其注入到客户端中。
依赖注入的基本用法:
using Microsoft.Extensions.DependencyInjection;
using System;
namespace dsjia
{
class Program
{
static void Main(string[] args)
{
var sc = new ServiceCollection(); // 依赖注入首先是创建一个容器
sc.AddScoped<CarM, Car>(); // 注册 CarM 接口到 Car 类的实现
sc.AddScoped<Truck, SmallTank>(); // 注册 Truck 接口到 Tank 类的实现
// sc.AddScoped<Truck, Tank>();
//反正就是想要去实现那个类之间去修改类的名称上去就可以了
var sp = sc.BuildServiceProvider();
CarM car = sp.GetService<CarM>(); // 获取 CarM 的实例
car.Run();
Truck truck = sp.GetService<Truck>(); // 获取 Truck 的实例
truck.Fire();
truck.Run();
}
}
class Drive
{
private CarM car;
public Drive(CarM car)
{
this.car = car;
}
public void Driver()
{
car.Run();
}
}
interface CarM
{
void Run();
}
class Car : CarM
{
public void Run()
{
Console.WriteLine("现在汽车开始奔跑");
}
}
interface Truck : CarM
{
void Fire();
}
class Tank : Truck
{
public void Fire()
{
Console.WriteLine("坦克开火!");
}
public void Run()
{
Console.WriteLine("现在是继承过来然后进行重写的,坦克快跑!!!");
}
}
class SmallTank : Truck
{
public void Fire()
{
Console.WriteLine("小坦克 boom...");
}
public void Run()
{
Console.WriteLine("小坦克 Run...");
}
}
}
1、首先是创建了一个ServiceCollection()的容器,你没有容器的话那么依赖注入往哪里注入呢
2、调用 sc.AddScoped<>(),注册Truck接口到Car类实现
3、使用 sc.BuildServiceProvider() 构建了一个 ServiceProvider 的实例 sp,该实例是实际进行依赖解析和注入的容器。
4、调用 sp.GetService<Tank>(),通过容器获取了一个 Tank 的实例,并赋值给 tank 变量。
5、调用实例Tank类中的方法