C#基础--委托

C#基础–委托

C#基础–委托

简单说它就是一个能把方法当参数传递的对象,而且还知道怎么调用这个方法,同时也是粒度更小的“接口”(约束了指向方法的签名)

一、什么是委托,委托的本质是什么?

  1. 跟方法有点类似,有参数,返回值,访问修饰符+ delegate

    public delegate void NoReturnNoPara();
    
    
  2. 委托的本质是一个类,继承自一个特殊类 MulticastDelegate,我们自己在定义类的时候是无法去继承MulticastDelegate

  3. 在类的内部生成一个类

image-20220324193835206

二、委托的实例化,执行委托

  1. ILSply反编译–委托的本质其实是一个类

  2. 委托本质是一个类,这个类的构造函数参数—Method–方法

image-20220324195245980

  1. 委托可以通过New来实例化,要求传递一个和这个委托的参数和返回值完全匹配的方法【完全匹配】
  • 委托有几个参数,方法参数数量也要对应;参数的类型在顺序上也要一一对应
  • 委托 返回参数和 方法 返回的参数类型也需要对应
public delegate void NoReturnWithPara(int x, int y);
private static void NoReturnNoParaMehtod()
{
 Console.WriteLine("这是一个无参数无返回值的方法。。。");
}
  1. 委托的实例–可以直接指向一个和这个委托参数+返回值完全匹配的方法

-语法糖–编译器给我们提供额便捷功能–new WithReturnNoPara();省略掉了

WithReturnNoPara withReturnNoPara1 = new WithReturnNoPara(WithReturnNoParaMehtod);
//可以省略
WithReturnNoPara withReturnNoPara2 = WithReturnNoParaMehtod;
  1. 执行委托实例的Invoke方法–去执行这个委托实例化的指向的这个方法—执行方法
//调用
NoReturnWithPara noReturnWithPara = new NoReturnWithPara(NoReturnNoParaMehtod);
noReturnWithPara.Invoke(55,68);
  1. 可以执行这个实例内部的三个方法:
//如果委托定义没有参数;在Inovke也没有参数 
withReturnNoPara.Invoke();	

//开启一个新的线程去执行委托
withReturnNoPara.BeginInvoke(null, null);	

//回调
withReturnNoPara.EndInvoke();
  1. 多种实例化:new、 直接指向一个方法,指向一个Lambad表达式
//new
WithReturnNoPara withReturnNoPara1 = new WithReturnNoPara(WithReturnNoParaMehtod);

//直接指向一个方法
WithReturnNoPara withReturnNoPara2 = WithReturnNoParaMehtod;

//指向一个Lambad表达式
NoReturnWithPara noReturnWithPara3 = (x, y) => { };

三、框架内置委托 Func 和 Action

.NET Framework3.0时代就开始拥有的产物

1. Action

Action是来自于System.RunTime的一个声明好的可以带有一个或者多个参数的委托delegate,没有返回值的委托(最多支持16个入参)

private void NoreturnNopara()
{

}

Action action = new Action(NoreturnNopara);
action.Invoke(); 
private void DoNothingInt(int i)
{
    Console.WriteLine("This is DoNothing");
}

Action<int> action1 = new Action<int>(DoNothingInt);
action.Invoke(); 
Action<int, List<string>, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object> action2 = null;

2. Func

  • 是来自于System.RunTime的一个声明好的可以有返回值的委托delegate,可以有参数 ,也可以没有参数(最多支持16个入参)

    private int ReturnNopara()
    {
        return 0;
    }
    
    Func<int> func = new Func<int>(ReturnNopara);
    
  • 如果既有参数 + 返回值,那<> 中前面类型参数=输入参数,最后的类型参数=返回值

    Func<int, int> func1 = new Func<int, int>(ToInt);
    
    Func<int, string, int> func2 = new Func<int, string, int>(DoNothingIntAndStringNew);
    
    Func<int, List<string>, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int> func3 = null;
    

问题一: 我们自己是可以定义委托的,为什么系统框架要给我们提供这样的两个委托呢?

答案一:【做到委托类型的统一】既然是系统框架给我们定义好了这两个委托,自然是希望我们在以后的开发中都去只是用这两个委托,这样就可以把委托做到统一;我们以后在使用委托的时候,就不用自己再去定义这个委托了;

场景:

//正常
new Thread(new ThreadStart(DoNoting));
//报错
new Thread(new NoReturnNoPara(DoNoting));

//为什么ThreadStart 和 NoReturnNoPara都是没有参数没有返回的委托,但是传入NoReturnNoPara会报错呢?
//因为委托本质是一个类,你传入的实例类型和定义的参数类型不一致,当然会报错啦
//系统框架给我们定义好了这两个委托,自然是希望我们在以后的开发中都去只是用这两个委托

问题二: 那之前定义好的委托呢?

答案二: 【历史包袱】我们是去不掉的,这被称之为历史包袱;

**问题三:**什么情况下可以考虑使用委托?

**答案三:**方法内部业务逻辑耦合严重;如果多个方法中出现了很多重复代码–去除重复代码,逻辑重用,考虑委托,达到逻辑解耦的目的。

  • 从上而下:代码稳定,不需要修改;执行的逻辑不在同一个方法中–逻辑解耦
  • 从下而上:去掉了重复代码,逻辑重用

四、委托的嵌套使用(俄罗斯套娃)

Asp .Net Core的核心设计,委托实现中间件的应用;

俄罗斯套娃,属于番外装饰器–AOP支持;就是把委托这个箱子一层一层的包装起来,将要执行的核心业务逻辑封装在最内部的箱子中,执行顺序是从最外层开始到最内层,在一层一层执行出来。

每一层执行的时候,我们就可以增加一些其他的业务逻辑;-- AOP的支持,装饰器

经过特性+委托 多层嵌套的封装–程序的执行环节的自动装配;如果有十个环节,那么只需要定义10个特性,分别标记就可以增加一个处理环节–ASP.NET Core的管道处理模型。

image-20220325212909007

普通类 InvokerAction.cs – Method方法

public class InvokerAction
{
    [BeforeWriteLogAttribute]
    [BeforeMethodAttribute]
    public void Method(int i)
    {
        Console.WriteLine("这里是要执行的核心业务逻辑");
    }
}

普通方式:

InvokerAction invokerAction = new InvokerAction();
invokerAction.Method(256);

反射:

InvokerAction invokerAction = new InvokerAction();
Type type = invokerAction.GetType();
MethodInfo methodInfo = type.GetMethod("Method");
methodInfo.Invoke(invokerAction, new object[] { 123 });

反射+委托:

public delegate void ShowDelegate();

public static void Show()
{
    InvokerAction invokerAction = new InvokerAction();
    Type type = invokerAction.GetType();
    MethodInfo methodInfo = type.GetMethod("Method");
    
    //实例化一个委托:委托内部包含了一个行为: 行为:执行Method方法
    ShowDelegate showMethod = new ShowDelegate(() =>
                                               {
                                                   methodInfo.Invoke(invokerAction, new object[] { 123 });
                                               }); 
    showMethod.Invoke();
}

套娃–多层委托的嵌套:

ShowDelegate showmthod1 = new ShowDelegate(() =>
                                           {
                                               showMthod.Invoke();
                                           });

ShowDelegate showmthod2 = new ShowDelegate(() =>
                                           {
                                               showmthod1.Invoke();
                                           });

ShowDelegate showmthod3 = new ShowDelegate(() =>
                                           {
                                               showmthod2.Invoke();
                                           });

ShowDelegate showmthod4 = new ShowDelegate(() =>
                                           {
                                               showmthod3.Invoke();
                                           });
//执行
showmthod4.Invoke();

定义一个特性:在特性中定义每个环节中要执行的动作:

抽象类–AbstractMethodAttribute.cs

public abstract class AbstractMethodAttribute : Attribute
{
    /// <summary>
    /// 在xxx 执行之前执行的点业务落
    /// </summary>
    public abstract ShowDelegate Do(ShowDelegate action);
}

BeforeWriteLogAttribute.cs

public class BeforeWriteLogAttribute : AbstractMethodAttribute
{
    /// <summary>
    /// 在xxx 执行之前执行的点业务落
    /// </summary>
    public override ShowDelegate Do(ShowDelegate action)
    { 
        ShowDelegate actionResult = new ShowDelegate(() =>
                                                     {
                                                         {
                                                             Console.WriteLine("前面写一个日志");  //这里可以随便写。。。。
                                                         }
                                                         action.Invoke();
                                                         {
                                                             Console.WriteLine("后面写一个日志");  //这里可以随便写。。。。
                                                         }
                                                     });
        return actionResult;
    }
}

BeforeMethodAttribute.cs

public class BeforeMethodAttribute : AbstractMethodAttribute
{
    /// <summary>
    /// 在xxx 执行之前执行的点业务落
    /// </summary>
    public override ShowDelegate Do(ShowDelegate action)
    {
        ShowDelegate actionResult = new ShowDelegate(() =>
                                                     {
                                                         {
                                                             Console.WriteLine("在xxx 执行之前执行点业务逻辑");  //这里可以随便写。。。。
                                                         }

                                                         action.Invoke();

                                                         {
                                                             Console.WriteLine("在xxx 执行之后执行点业务逻辑");  //这里可以随便写。。。。
                                                         }
                                                     });
        //actionResult.Invoke(); 
        return actionResult;
    }
}

调用:

public static void Show()
{
    InvokerAction invokerAction = new InvokerAction();
	Type type = invokerAction.GetType();
	MethodInfo methodInfo = type.GetMethod("Method");
    
	ShowDelegate showMethod = new ShowDelegate(() =>
                                               {
                                                   methodInfo.Invoke(invokerAction, new object[] { 123 });
                                               }); 
    //如果方法上存在继承于自AbstractMethodAttribute的属性
    if (methodInfo.IsDefined(typeof(AbstractMethodAttribute), true))
    {
        //获取所有属性
        foreach (AbstractMethodAttribute attribute in methodInfo.GetCustomAttributes().Reverse())
        {
            //第一次循环时,showMethod.Invoke 交于第一个属性执行
            //接下来得每次执行,都是下一个属性执行上一个属性返回来得委托
            //最后形成一个俄罗斯套娃,值得注意的时候,这时候所有委托并没有实际执行
            showMethod = attribute.Do(showMethod);	
        }
    }
	//返回的最后一个委托开始执行,即从套娃的最外层开始执行,最终到 showMethod.Invoke -> methodInfo.Invoke;
    showMethod.Invoke();
}

五、多播委托

  1. 多播委托:我们声明的任何一个委托都是多播委托;任何一个委托都是继承自MulticastDelegate(多播委托),定义的所有的委托都是多播委托;

  2. 作用:可以通过+= 把多个方法添加到这个委托中去;形成一个方法的执行链;利用 action1.Invoke() 执行委托的时候,按照添加方法的顺序,依次去执行委托;

    Action action = new Action(DoNothing);
    action += DoNothingStatic;
    action += new Student().Study;
    action += Student.StudyAdvanced;
    action += DoNothing;
    action += () =>
    {
        Console.WriteLine("this is 拉姆达表达式。。。");
    };
    
  3. action.BeginInvoke(); //开启一个新的线程 去执行委托

    注册有多个方法的委托,不能使用BeginInvoke;如上述action, 因添加多个方法,所以不能使用 BeginInvoke直接执行,需要用到第四点的 action.GetInvocationList() 获取委托列表循环执行;

  4. 想要开启一个新的线程去执行委托呢?

    通过action.GetInvocationList() 获取到所有的委托,然后循环,每个方法执行的时候可以BeginInvoke

    foreach (Action action1 in action.GetInvocationList())
    {
        //action1.Invoke();
        action1.BeginInvoke(null,null);
    }
    
  5. 多播委托也可以通过-=移除方法

    是从后往前,逐个匹配,如果匹配不到,就不做任何操作就不做任何操作;如果匹配到,就把当前这个移除,且停止去继续往后匹配;

    action -= DoNothing;
    
  6. 在移除的方法的时候,必须是同一个实例,同一个方法才能移除;

    action -= new Student().Study;	//没有移除掉:因为不是同一个实例
    
  7. lambda表示式是不能移除的;

    虽然方法体内容是一样的,但是lambada表示在底层会生成不同的方法

    action -= () => //没有移除:  其实是因为不是同一个方法---lambda表达式--在底层会生成不同的方法
    {
        Console.WriteLine("this is 拉姆达表达式。。。");
    };
    

六、观察者模式

事例:猫的故事

Cat.cs

public void Miao()
{
    Console.WriteLine("{0} Miao", this.GetType().Name);
    new Dog().Wang(); //狗叫了
    new Mouse().Run();//老鼠跑了
    new Baby().Cry(); // 小孩哭了
}

缺点:

  • 职责不单一 — 一个类中包含的职责太多了,除了Miao 一声,依赖于其他的类太多;
  • 依赖太重–依赖于Dog、Mouse、…—代码不稳定;Dog、Mouse、…任何一个类的修改,都有可能会影响到这只猫;
  • 这完全是面向过程式编程–完全是翻译需求;

改造目的:

  • 职责单一: 猫的职责仅仅只是Miao一下,其他的动作,不是猫的动作;
  • 猫Miao一声后会引发一些列的动作,应该放出去,不能属于这只猫
  • 猫Miao一声后,触发一些列的动作—不能把这一些列的动作放在猫的内部

接口类 IObject.cs:

public interface IObject
{
    void DoAction();
}

观察者们: 【订阅者】

public class Dog : IObject
{
    public void DoAction()
    {
        this.Wang();
    }
    public void Wang()
    {
        Console.WriteLine("{0} Wang", this.GetType().Name);
    }
}
public class Mouse : IObject
{
    public void DoAction()
    {
        this.Run();
    }
    public void Run()
    {
        Console.WriteLine("{0} Run", this.GetType().Name);
    }
}
public class Baby : IObject
{
    public void DoAction()
    {
        this.Cry();
    }

    public void Cry()
    {
        Console.WriteLine("{0} Cry", this.GetType().Name);
    }
}

发布者1:Cat.cs 【委托】

  • 通过定义委托属性
  • 给委托+=方法–Miao一声之后,要触发的动作
  • 特点:把要执行的动作不放在猫的内部从外面去指定
  • 多播委托+=来完成了一系列动作的转移,把猫Miao一声后的动作转移到上端
  • 去掉了猫对于其他的依赖—提高了猫的稳定性
  • 通过委托实现了观察者模式–观察者模式—把不属于我的动作转移出去—甩锅
public  Action MiaoAction = null; 
/// <summary>
/// 这个方法仅仅只是Miao一声
/// 引发的动作---可以放到多播委托中去
/// </summary>
public void MiaoDelegate()
{
    Console.WriteLine("{0} MiaoDelegate", this.GetType().Name);
    MiaoAction?.Invoke();//?. 如果不为null,就执行后面的动作; 若event不为null,则invoke,这是C#6的新语法。 ?.称为空值传播运算符。
}
cat.MiaoAction += new Dog().Wang; //狗叫了
cat.MiaoAction += new Mouse().Run;//老鼠跑了
cat.MiaoAction += new Mother().Wispher;
cat.MiaoAction += new Baby().Cry; // 小孩哭了
cat.MiaoDelegate();	//执行1

cat.MiaoAction();	//和执行1一样,也可以执行

发布者2:Cat.cs 【接口 + 列表 + 循环执行】

public List<IObject> observerlist = new List<IObject>();	//IObject 含有 DoAction方法

public void MiaoObsever()
{
    Console.WriteLine("{0} MiaoObsever", this.GetType().Name);
    if (observerlist.Count>0)
    {
        foreach (var item in observerlist)
        {
            item.DoAction();
        }
    }
}
cat.observerlist.Add(new Dog()); //狗叫了
cat.observerlist.Add(new Mouse());//老鼠跑了
cat.observerlist.Add(new Baby()); // 小孩哭了
cat.MiaoObsever();

两者从本质上来说是没有什么太大的区别

第一种是通过多播委托来支持,是语法层面来支持;

第二种是通过面向对象的方法来完成,是程序设计层面来支持;

七、事件

发布者3:Cat.cs 【事件】

public event Action MiaoEventHanlder = null;

public void MiaoEnvent()
{
    Console.WriteLine("{0} MiaoEnvent", this.GetType().Name);
    MiaoEventHanlder?.Invoke();//若event不为null,则invoke,这是C#6的新语法。 ?.称为空值传播运算符。
}
cat.MiaoEventHanlder += new Dog().Wang; //狗叫了
cat.MiaoEventHanlder += new Mouse().Run;//老鼠跑了
cat.MiaoEventHanlder += new Mother().Wispher;
cat.MiaoEnvent(); 

cat.MiaoEventHanlder.Invoke(); //不能执行

问题一:委托和事件有什么联系和区别?

  • 事件是委托的实例,事件是特殊的委托;

  • 事件和委托都可以使用 +=-= ;

问题二:既然有了多播委托,为什么又搞个事件呢?

安全

  • 事件是只能在类的内部执行,就算是你的子列也不能执行当前类中的事件,只能由自己来执行
  • 事件从外面操作,只能有一个 += / -=

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/41379.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

云迁移第二波热潮来袭,你准备好了吗?

最近&#xff0c;云迁移再次被频繁提及&#xff0c;企业对云迁移的需求量有回升趋势&#xff0c;究其根本&#xff0c;主要有以下原因&#xff1a; 企业数字化进程加速&#xff0c;本地上云需求强劲 根据《2021中国企业上云指数洞察报告》&#xff0c;我国实体经济上云渗透率…

深入理解Java虚拟机(二)Java内存区域与内存溢出异常

一、前言 对于Java程序员来说&#xff0c;在虚拟机自动内存管理机制的帮助下&#xff0c;不再需要为每一个new操作去写配对的delete/free代码&#xff0c;不容易出现内存泄漏和内存溢出问题&#xff0c;看起来由虚拟机管理内存一切都很美好。不过&#xff0c;也正是因为Java程序…

kafka接收外部接口的数据,并实现转发

目录 一、什么是kafka 二、kafka接收外部接口数据 三、kafka收到数据后转发 四、kafka总结 一、什么是kafka Kafka是一种分布式流式处理平台&#xff0c;最初由LinkedIn开发。它设计用于高吞吐量、低延迟的数据处理&#xff0c;能够处理大规模的实时数据流。Kafka采用发布…

关系型数据库设计规则

目录 1.1 表、记录、字段 1.2 表的关联关系 1.2.1 一对一关联&#xff08;one-to-one&#xff09; 1.2.2 一对多关系&#xff08;one-to-many&#xff09; 1.2.3 多对多&#xff08;many-to-many&#xff09; 1.2.4 自我引用&#xff08;Self reference&#xff09; 关系…

用Python采用Modbus-Tcp的方式读取485电子水尺数据

README.TXT 2023/6/15 V1.0 实现了单个点位数据通信、数据解析、数据存储 2023/6/17 V2.0 实现了多个点位数据通信、数据解析、数据存储 2023/6/19 V2.1 完善log存储&#xff0c;仅保留近3天的log记录&#xff0c;避免不必要的存储&#xff1b;限制log大小&#xff0c;2MB。架…

基于Redisson的Redis结合布隆过滤器使用

一、场景 缓存穿透问题 一般情况下&#xff0c;先查询Redis缓存&#xff0c;如果Redis中没有&#xff0c;再查询MySQL。当某一时刻访问redis的大量key都在redis中不存在时&#xff0c;所有查询都要访问数据库&#xff0c;造成数据库压力顿时上升&#xff0c;这就是缓存穿透。…

成为一个年薪30W+的DFT工程师是一种什么体验?

一直以来&#xff0c;DFT都是数字IC设计行业中相对神秘的一个岗位。 你说他重要吧&#xff0c;并不是所有芯片设计公司都有这个岗位&#xff0c;你说他不重要吧&#xff0c;但凡芯片产品达到一定规模后&#xff0c;就必须设置DFT部门。 一、什么是DFT&#xff1f; DFT&#x…

【分布式应用】ceph分布式存储

目录 一、存储基础1.1单机存储设备1.2单机存储的问题1.3分布式存储的类型 二、Ceph简介2.1Ceph 优势2.2Ceph 架构2.3Ceph核心组件OSD&#xff08;Object Storage Daemon&#xff0c;守护进程 ceph-osd&#xff09;PG&#xff08;Placement Group 归置组&#xff09;PoolMonitor…

SpringBoot错误: 找不到或无法加载主类

1.一般出现这种情况都是配置文件application.properties出现的问题 2.可以尝试 maven clean install 以及rebuild project 3.删除项目里.idea文件 重新导入至IDEA编辑器 选择Maven项目 配置好maven.xml 后重新导入

解决GitHub下载速度太慢问题的方法汇总(持续更新,建议收藏)

文章目录 前言一、使用 git clone --depth1 来下载二、修改host文件解决三、谷歌浏览器插件加速四、油猴插件和脚本五、gitclone.com六、Github 加速下载链接七、Github 镜像访问八、使用码云下载参考资料&#xff0c;感谢以下文章 前言 Github上下载仓库或者克隆仓库&#xf…

Docker基础——Centos7安装Docker

0.安装Docker Docker 分为 CE 和 EE 两大版本。CE 即社区版&#xff08;免费&#xff0c;支持周期 7 个月&#xff09;&#xff0c;EE 即企业版&#xff0c;强调安全&#xff0c;付费使用&#xff0c;支持周期 24 个月。 Docker CE 分为 stable test 和 nightly 三个更新频道…

「深度学习之优化算法」(十三)蝙蝠算法

1. 蝙蝠算法简介 (以下描述,均不是学术用语,仅供大家快乐的阅读)   蝙蝠算法(Bat Algorithm)是受蝙蝠回声定位的特性启发而提出的新兴算法,提出时间是2010年,虽然距今(2020)有近10年,但与其它的经典算法相比仍算一个新算法。算法也已有一定规模的研究和应用,但仍…

数据结构 ~ 栈、队列

栈 一个后进先出的数据结构、JS中没有栈&#xff0c;可以使用 Array 模拟 const stack [] stack.push(1) // 入栈 stack.push(2) // 入栈 const item1 stack.pop() // 出栈 const item2 stack.pop() // 出栈以上代码可以使用 nodeJs 断点调试&#xff08;F5启动&#xff0…

【Envi风暴】Envi5.6安装图文教程(附Envi5.6完整版下载)

本文讲解Envi5.6与应用商店app store的安装与使用。 文章目录 一、ENVI5.6安装过程二、app store的安装三、ENVI5.6下载地址一、ENVI5.6安装过程 从文末网盘下载完整的ENVI5.6安装包,如下所示:双击主程序envi56-win.exe,开始安装。 点击Next。 点击Next。 选择安装路径,可…

3.15 Bootstrap 警告(Alerts)

文章目录 Bootstrap 警告&#xff08;Alerts&#xff09;可取消的警告&#xff08;Dismissal Alerts&#xff09;警告&#xff08;Alerts&#xff09;中的链接 Bootstrap 警告&#xff08;Alerts&#xff09; 本章将讲解警告&#xff08;Alerts&#xff09;以及 Bootstrap 所提…

JDK、JRE、JVM之间的关系是什么?

目录 JVM、JRE、JDK的关系&#xff1f; JDK、JRE、JVM都是什么&#xff1f; JVM JRE JDK JVM、JRE、JDK的关系&#xff1f; 三者包含关系&#xff1a; JDK>JRE>JVM JDK、JRE、JVM都是什么&#xff1f; jdk&#xff1a;是用于java开发的最小环境 包括&#xff1a;ja…

8.postgresql--Update join 和 Delete using

Update join Update join用于基于另一张表更新表数据&#xff0c;语法如下&#xff1a; UPDATE t1 SET t1.c1 new_value FROM t2 WHERE t1.c2 t2.c2;CREATE TABLE product_segment (id SERIAL PRIMARY KEY,segment VARCHAR NOT NULL,discount NUMERIC (4, 2) );INSERT INTO…

【JavaEE】DI与DL的介绍-Spring项目的创建-Bean对象的存储与获取

Spring的开发要点总结 文章目录 【JavaEE】Spring的开发要点总结&#xff08;1&#xff09;1. DI 和 DL1.1 DI 依赖注入1.2 DL 依赖查询1.3 DI 与 DL的区别1.4 IoC 与 DI/DL 的区别 2. Spring项目的创建2.1 创建Maven项目2.2 设置国内源2.2.1 勾选2.2.2 删除本地jar包2.2.3 re…

数据中心机房建设,务必确定这13个关键点

下午好&#xff0c;我的网工朋友。 关于机房、机架的相关内容&#xff0c;给你们说了不少。 今天再给你补充个知识点&#xff0c;机房建设&#xff0c;要怎么做。 熟悉机房建设的网工朋友可能都知道&#xff0c;一个全面的数据中心机房建设工程一般包括&#xff1a; 综合布…

VUE- 选取本地图片,自定义裁切图片比例 vue-cropper

裁切图片&#xff0c;按照比例裁切&#xff0c;分步骤 1&#xff1a;el-upload选择本地图片&#xff08;分选择本地和上传两步骤&#xff09; 2&#xff1a;在on-change回调方法中拿到el-upload选中的图片&#xff0c;显示在vueCropper上&#xff08;&#xff09;。 2.1&…