首页 > 编程语言 >C#委托的使用

C#委托的使用

时间:2024-12-22 09:57:35浏览次数:7  
标签:Product 委托 C# product 使用 new 方法 public

从三个维度学习委托(根据刘铁猛老师做的笔记)
  1. C#中自带的委托
  2. 自定义委托的学习
  3. 委托的应用

一、C#自带的委托


1.程序的本质是数据加算法


变量(数据)的本质:以变量名最对应的内存地址为起点的一段内存,在这段内存中存储的就是变量的数据,内存多大就是有数据类型决定的。
函数(算法):也是地址,是函数名所对应的内存地址为起点的一段内存,这段内存不是一个值,而是存储的一组机器语言指令。
无论是数据还是算法,都是保存在内存中。变量是用来寻找数据的地址,函数是用来寻找算法的地址。

2.直接调用与间接调用的本质:


直接调用:通过函数名来调用函数,CPU通过函数名直接获取函数所在地址开始执行,直到最后一行然后返回到调用的位置。
间接调用:通过指向某个函数的函数指针来调用函数,函数指针是指向函数的一个指针,函数指针所存储的的就是函数名所对应的地址。通过函数指针,CPU一样可以找到存储那段机器语言的内存。

使用C#自带的委托-示例:

  1. Action:无返回值的委托使用Action
  2. Func:有返回值的委托使用Func
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp4
{

    class Program
    {
        static void Main(string[] args)
        {
            //创建实例
            Calculator calculator = new Calculator();
            //创建action 数据类型
            //C#中已有的委托 -》action    ;在此间接调用只是需要report方法的方法名,不需要加括号
            Action action = new Action(calculator.Report);
            //Report方法的直接调用
            calculator.Report();
            //report方法的间接调用
            action.Invoke();

            //可以指向add与sub 方法的委托=> function
            Func<int,int,int>  func1= new Func<int, int, int>(calculator.Add);
            Func<int,int,int>  func2= new Func<int, int, int>(calculator.Sub);

            int x =300;
            int y = 100;
            int z;

            //Add方法的间接调用
            z = func1.Invoke(x, y);
            Console.WriteLine(z);
            //Sub方法的间接调用
            z = func2.Invoke(x, y);
            Console.WriteLine(z);

        }
    }
    class Calculator
    {
        public void Report()
        {
            Console.WriteLine("i have 3 methods ");
        }
        public int Add(int x, int y)
        {
            int result = x + y;
            return result;
        }
        public int Sub(int x, int y)
        {
            int result = x - y;
            return result;
        }
    }
}

在使用Func委托根据我们写的方法,可以看到func的补充:传入为两个int,out也是int。因此:add方法与sub方法是符合的。


二、自定义委托

委托是一种,类是一种数据类型(引用类型的数据类型)。
用类可以声明变量,可以创建实例
所以委托可以声明变量,创建实例.

委托是一种类,要声明在命名空间中,这样就可以跟其他的类处在同一个级别了。

下面声明委托(自定义委托)-示例:

using System.Data.SqlTypes;
using System.Runtime.Intrinsics;

namespace ConstructorExample
{
    //声明委托
    public delegate double Cal(double x, double y);   //第一个double为目标方法的返回值                                                                                  
                                                     //Cal 为委托名,括号内为目标方法的参数列表
    class program
    {
        static void Main()
        {
            //首先创建Calculator类的实例,有了这个实例,我们才可以访问这个实例的方法
            Calculator calculator = new Calculator();
            Cal cal1 = new Cal(calculator.Add); //此时传入的方法必须满足返回值为double并且参数列表为两个double
                                          //calculator.Add不加括号是因为此处间接访问方法,与直接访问不同。
            Cal cal2 = new Cal(calculator.Sub);
            Cal cal3 = new Cal(calculator.Mul);
            Cal cal4 = new Cal(calculator.Div);
            double a = 10;
            double b = 10;
            double c;


            c= cal1.Invoke(a, b);
            Console.WriteLine(c);
            c= cal2.Invoke(a, b);
            Console.WriteLine(c);
            c= cal3.Invoke(a, b);
            Console.WriteLine(c);
            c= cal4.Invoke(a, b);
            Console.WriteLine(c);
        }
    }
    class Calculator
    {

        public double Add(double x, double y)
        {
            return x + y;
        }
        public double Sub(double x, double y)
        {
            return x - y;
        }
        public double Mul(double x, double y)
        {
            return x * y;
        }
        public double Div(double x, double y)
        {
            return x / y;
        }
    }
}

注:如果委托声明在其他program类中,编译、运行都可以正常进行。但是Cal委托就不是一个独立的类了,而是program的嵌套类。
如下:(在声明cal1,2,3,4是前面的program.Cal也可以直接写为Cal,因为Cal本身就写在了program中)

要注意的是:声明的委托要与它所封装的方法(所指向的目标方法)必须保持类型兼容。

类型兼容就是声明委托的时候所使用的的目标方法的返回值类型,以及目标方法的 参数列表类型必须要与目标方法的返回值类型和参数列表一致。


三:委托的使用

在正常的使用中,我们都会把委托当做方法的参数传进方法中。这么做的好处就是,当你写了一个方法,这个方法有一个委托类型的参数(委托会封装着一个方法),当我们在方法体中使用传进来的委托,间接的调用委托所封着的那个方法。这样就会形成动态调用方法的代码结构。

把委托当做参数传进方法的用法有两种:

  1. 模版方法
  2. 回调方法

1.模版方法:

        当你写了一个方法,通过传进来的委托参数,借用指定的外部方法,来产生一个结果。(相当于在写的方法中有一个填空题,这个填空题的空白处就用传进来的委托参数间接的调用指定的外部方法)。 这个方法一般是有返回值的,当拿到返回值后,继续执行方法后的逻辑。

          就类似于你写了一个模版,这个模版有一处是不确定的,其余部分是确定好的。这个不确定的部分就通过传进来的委托类型的参数所包含的方法进行填补。

  

下面是不使用委托的示例(只是方法简单的调用):

using System.Data.SqlTypes;
using System.Runtime.Intrinsics;

namespace DelegateExample
{
    class Program
    {
        static void Main(string[] args)
        { 
            WrapFactory wrapFactory = new WrapFactory();
            Box box = wrapFactory.WrapProduct();
            Console.WriteLine(box.Product.Name);
        }
    }

    class Product  //定义产品属性
    {
        public string Name { get; set; }
    }
    class Box  //定义包装盒,用于包装产品
    {
        public Product Product { get; set; }
    }

    class MakeProduct //定义生产工厂,用于生产产品Product
    {
        public Product MakePizza()
        {
            Product product = new Product();
            product.Name = "Pizaa";
            return product;
        }
        public Product MakeToyCar()
        {
            Product product = new Product();
            product.Name = "ToyCar";
            return product;
        }
    }

    class WrapFactory //定义包装工厂,将生产工厂MakeProduct的产品Product包装到包装盒Box中
    {
        
        public Box WrapProduct()
        {
            MakeProduct makeProduct1 = new MakeProduct();
            Product product = makeProduct1.MakePizza();
            Box box = new Box();    
            box.Product = product;
            return box;
        }
    }

}

下面为使用委托的用例:

using System.Data.SqlTypes;
using System.Runtime.Intrinsics;

namespace DelegateExample
{
    class Program
    {
        static void Main(string[] args)
        { 
            MakeProduct makerProduct = new MakeProduct();

            Func<Product> func1 = new Func<Product>(makerProduct.MakePizza);
            Func<Product> func2 = new Func<Product>(makerProduct.MakeToyCar);

            WrapFactory wrapFactory = new WrapFactory();
            Box box1  = wrapFactory.WrapProduct(func1);
            Console.WriteLine(box1.Product.Name);
            Box box2  = wrapFactory.WrapProduct(func2);
            Console.WriteLine(box2.Product.Name);

        }
    }

    class Product  //定义产品属性
    {
        public string Name { get; set; }
    }
    class Box  //定义包装盒,用于包装产品
    {
        public Product Product { get; set; }
    }

    class MakeProduct //定义生产工厂,用于生产产品Product
    {
        public Product MakePizza()
        {
            Product product = new Product();
            product.Name = "Pizaa";
            return product;
        }
        public Product MakeToyCar()
        {
            Product product = new Product();
            product.Name = "ToyCar";
            return product;
        }
    }

    class WrapFactory //定义包装工厂,将生产工厂MakeProduct的产品Product包装到包装盒Box中
    {
        
        public Box WrapProduct(Func<Product> getProduct)//(Func<Product> getProduct封装一个方法,方法的返回值类型为Product
        {
            Product product =getProduct.Invoke();  //getProduct.Invoke()指向委托封装的方法所在的位置
            Box box = new Box();    
            box.Product = product;
            return box;
        }
    }

}

可以通过上述对来来看使用委托封装方法传入方法与直接调用方法的区别。可以看出来使用委托更加方便。可重点查看Main 与class WrapProduct 来对比:


2.回调方法:

        回调(Call back),回调指的一种场景是:有一天我遇到了A,我把名片给A,并告知A需要我的时候给我打电话,如果需要我的时间,A会call 我,不需要我的时间,A永远不会call 我。这就类似于在代码中A调用我一样,如果A需要我就会调用我,不需要我就永远不需要调用我。这样就构成了回调方法的关系。(而且回调方法给我们一个机会,我们可以动态的选择后续将被调用的方法。)

        当以回调方法的形式来使用委托的时候,我们要做的是把委托类型的参数传入主调方法里面去,被传入主调方法中的委托类型的参数里面会封装着被回调的方法,主调函数会根据自己的逻辑觉得要不要调用回调方法。整个代码的逻辑像一个流水线,一般情况下,主调方法会在主要逻辑执行完之后决定要不要调用回调方法。

        回调方法一般位于主调方法代码的末尾,而且回调方法一般的用处是执行后续的工作,构成一个流水线的结果,所以说回调方法一般是没有返回值的。

        

回调方法的应用:

using System.Data.SqlTypes;
using System.Runtime.Intrinsics;

namespace DelegateExample
{
    class Program
    {
        static void Main(string[] args)
        { 
            MakeProduct makerProduct = new MakeProduct();

            Func<Product> func1 = new Func<Product>(makerProduct.MakePizza);
            Func<Product> func2 = new Func<Product>(makerProduct.MakeToyCar);

            //使用回调方法
            Logger logger = new Logger();
            Action<Product> ac1 = new Action<Product>(logger.log);

            WrapFactory wrapFactory = new WrapFactory();
            Box box1  = wrapFactory.WrapProduct(func1,ac1);
            Console.WriteLine(box1.Product.Name);
            Box box2  = wrapFactory.WrapProduct(func2,ac1);
            Console.WriteLine(box2.Product.Name);

        }
    }
    class Logger
    {
        public void log(Product product)
        {
            Console.WriteLine("Product {0} create at {1} .Price is {2}.",product.Name,DateTime.UtcNow,product.Price);
        }
    }


    class Product  //定义产品属性
    {
        public string Name { get; set; }
        public double Price {  get; set; }
    }
    class Box  //定义包装盒,用于包装产品
    {
        public Product Product { get; set; }
    }

    class MakeProduct //定义生产工厂,用于生产产品Product
    {
        //模版方法为MakePizza、MakeToyCar
        public Product MakePizza()
        {
            Product product = new Product();
            product.Name = "Pizaa";
            product.Price = 20;
            return product;
        }
        public Product MakeToyCar()
        {
            Product product = new Product();
            product.Name = "ToyCar";
            product.Price = 190;
            return product;
        }
    }

    class WrapFactory //定义包装工厂,将生产工厂MakeProduct的产品Product包装到包装盒Box中
    {
        //(Func<Product> getProduct封装一个方法,方法的返回值类型为Product
        //Action<Product> logCallBack 为无返回值的委托 传入参数类型为Product
        public Box WrapProduct(Func<Product> getProduct,Action<Product> logCallBack)
        {
            Product product =getProduct.Invoke();  //getProduct.Invoke()指向委托封装的方法所在的位置
            if (product.Price > 50)
            {
                logCallBack(product);  //使用回调方法
            }
            Box box = new Box();    
            box.Product = product;
            return box;
        }
    }

}

标签:Product,委托,C#,product,使用,new,方法,public
From: https://blog.csdn.net/YCG8882888/article/details/144619904

相关文章

  • 使用CSS3实现照片左右飞入滑出切换
    要使用CSS3实现照片左右飞入滑出的切换效果,你可以利用CSS的transform和transition属性。下面是一个简单的示例,展示了如何实现这种效果:HTML结构:首先,你需要一个包含照片的HTML结构。这里,我们使用div元素来代表照片,并给它们添加类名以便于CSS样式化。<divclass="photo-containe......
  • 使用canvas实现一个贪吃蛇小游戏
    创建一个贪吃蛇游戏涉及到很多步骤,以下是一个简化版的贪吃蛇游戏实现方案,使用HTML5的<canvas>元素。HTML首先,在HTML中创建一个<canvas>元素。<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>贪吃蛇游戏</title><s......
  • 使用CSS3实现响应式win8 metro风格的页面
    要使用CSS3实现一个响应式的、类似Windows8Metro风格的页面,你需要考虑几个关键因素,包括布局、色彩、字体和交互设计。下面是一个基本的指南,帮助你开始这个项目:1.布局响应式设计:使用媒体查询(MediaQueries)来确保你的页面在不同屏幕尺寸上都能良好地显示。网格系统:可以采用Fl......
  • 使用HTML5实现窗户玻璃雨滴的逼真效果
    在前端开发中,实现窗户玻璃上雨滴的逼真效果需要结合HTML5、CSS3以及JavaScript,特别是利用CanvasAPI或者WebGL进行图形渲染。以下是一个基于CanvasAPI实现雨滴效果的简化示例:HTML结构:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="vi......
  • 使用CSS3实现动态信封折叠效果
    实现一个动态信封折叠效果需要用到一些CSS3动画和转换(transforms)。以下是一个简单的例子,展示了如何使用CSS3来创建一个信封折叠和展开的动画效果。首先,我们需要创建一个信封的基本结构。HTML结构可能如下所示:<divclass="envelope"><divclass="envelope-front"></div......
  • 使用HTML5实现一个倾斜的图片增效果
    在HTML5和CSS3中,你可以使用CSS3的transform属性来实现图片的倾斜效果。以下是一个简单的例子:HTML:<divclass="image-container"><imgsrc="your-image.jpg"alt="YourImage"class="tilted-image"></div>CSS:.image-container{......
  • 使用CSS3实现立体图片旋转的特效
    要使用CSS3实现立体图片旋转的特效,你可以利用transform属性,该属性允许你对元素进行旋转、缩放、倾斜或平移等操作。以下是一个简单的示例,展示了如何使用CSS3实现一个立体图片旋转的效果:HTML结构:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><meta......
  • 使用canvas画一个灰太狼的图案
    在前端开发中,使用<canvas>元素来绘制一个灰太狼的图案是一个相对复杂的过程,因为它涉及到细致的绘图技巧和可能的动画效果。下面是一个简化的步骤指南,以及一个基本的示例代码,用于在<canvas>上绘制一个类似灰太狼的简单图案。步骤指南设置Canvas:在HTML中添加一个<canvas>元素,并......
  • 使用CSS3实现模拟雷达波向外发散的动画效果
    要实现模拟雷达波向外发散的动画效果,我们可以使用CSS3的动画特性,如@keyframes和animation。以下是一个简单的示例,展示如何使用这些特性来创建这种效果:HTML结构:<divclass="radar-container"><divclass="radar-wave"></div></div>CSS样式:.radar-container{po......
  • 使用canvas画一个喜洋洋的图案
    在前端开发中,使用HTML5的<canvas>元素来绘制一个复杂的图案如“喜洋洋”可能是一项具有挑战性的任务,因为它需要一些绘图技能和耐心。由于“喜洋洋”是一个具体的卡通形象,我无法直接为你提供一个精确的绘图脚本,但我可以给你一个基本的框架和指导,帮助你开始这个任务。首先,你需要在H......