1.委托(delegate )

委托是一种类型,它可以存储对一个或多个方法的引用。它类似于C/C++中的函数指针,允许您将方法作为参数传递、存储和调用。

写法:

delegate <return_type> <delegate_name>( );

  • <return_type>:表示委托所引用方法的返回类型。
  • <delegate_name>:表示委托的名称。
  • <parameters>:表示委托所引用方法的参数列表。
//以下是一个示例,说明如何定义和使用委托:
delegate void MyDelegate(string message);

class Program
{
    static void Main()
    {
        MyDelegate del = new MyDelegate(ShowMessage);
        del("Hello, world!");
    }
    static void ShowMessage(string message)
    {
        Console.WriteLine(message);
    }
}

具体应用示例:

namespace WinFormsTest
{
    /// <summary>
    /// https://www.bilibili.com/video/BV1E14y1C7sm/?spm_id_from=333.337.search-card.all.click&vd_source=c17a6596481e29f0ffac1a21a026abff
    /// 委托有什么用?
    /// 1.将方法作为参数进行传递,有利于方法的解耦和隔离
    /// 2.声明事件并进行注册
    /// 定义委托的格式
    /// 1.声明委托类型
    /// 2.必须有一个方法包含了要执行的代码,这个方法必须符合委托类型签名
    /// 3.创建一个委托实例
    /// 4.必须调用invoke委托实例
    /// </summary>
    delegate void MyDelegate(); //定义一个委托,无参数和返回值
    delegate string MyDelegate2(int a, int b);
    public partial class delegateAndevent : Form
    {
        public delegateAndevent()
        {
            InitializeComponent();
            //Type type = typeof(myDelegate);
            //if (type.IsClass) MessageBox.Show("这是一个类");
            //MyDelegate myDelegate = new MyDelegate(Method1);
            下面两种方法都可以到委托中调用新的方法
            //MyDelegate myDelegate2 = new MyDelegate(Method2);
            //myDelegate += myDelegate2;
            myDelegate += Method2; //在委托中添加方法
            myDelegate -= Method2; //在委托中去除方法
            //myDelegate.Invoke();

            string result1 = myCal(5, 5, Add);
            string result2 = myCal(5, 5, Multiple);
            //MessageBox.Show(result1 + "..........." + result2);
        }

        void Method1()
        {
            MessageBox.Show("调用了方法1");
        }
        void Method2()
        {
            MessageBox.Show("调用了方法2");
        }

        String myCal(int a, int b, MyDelegate2 myDelegate)
        {
            return myDelegate(a, b);
        }

        //没有定义方法的修饰符时,private为默认,如果与namespace为同一级时则internal为默认
        string Add(int a, int b)
        {
            return (a + b).ToString();
        }
        string Multiple(int a, int b)
        {
            return (a * b).ToString();
        }

        private void btnDelegate_Click(object sender, EventArgs e)
        {
            //C#中的两种强委托类型 Action<T1> , Func<T1,T2,T3,Tresult>
            //Action 不返回值的委托类型 ,最多输入一个参数
            Action<string> aa1 = (string a) => { MessageBox.Show(a + "强委托类型 Action<T1>,第一次"); }; //使用lambda表达式
            Action<string> aa2 = a => { MessageBox.Show(a + "强委托类型 Action<T1>,第二次"); }; //使用lambda表达式
            aa1 += aa2;
            aa1.Invoke("使用了:");


            //Func<T1,T2,T3,Tresult>
            Func<string, string> func = h => h + "强委托类型 Func<T1,T2,T3,Tresult>,第一次";//使用lambda表达式
            Func<string, string, string> func2 = (h, b) => h + "强委托类型 Func<T1,T2,T3,Tresult>" + b;//使用lambda表达式
            //func += func2; //会报错,同一种类型的委托才可以
            string funcResult1 = func.Invoke("使用了:");
            string funcResult2 = func2.Invoke("使用了:", "第二次");
            MessageBox.Show(funcResult1);
            MessageBox.Show(funcResult2);
        }
    }
}

2.事件(event)

它用于建立一种通讯机制,使得对象可以通知系统某些事情已经发生,而无需知道具体会有哪些对象对此进行响应。

事件与委托: 事件是基于委托(一种特殊的类型安全的函数指针)的。

发布者与订阅者: 发布者负责声明和触发事件,订阅者负责订阅事件并定义响应逻辑。

事件定义: 使用event关键字和相应的委托类型来定义事件。

public delegate void MyEventHandler();
public event MyEventHandler MyEvent;

事件订阅: 使用+=操作符订阅事件,-=操作取消事件订阅。

publisher.MyEvent += MyMethod;
publisher.MyEvent -= MyMethod;

EventArgs: 标准做法是通过一个继承自EventArgs的类来传递事件数据。

事件访问器: 可以使用addremove关键字可以自定义事件的订阅和取消订阅逻辑。

private MyEventHandler myEvent;

public event MyEventHandler MyEvent
{
    add
    {
        myEvent += value;
    }
    remove
    {
        myEvent -= value;
    }
}

匿名方法和Lambda: 可以使用匿名方法或Lambda表达式作为事件处理器。

自动事件: 可以使用event关键字简化事件的定义和实现,这样编译器会自动为你实现事件访问器。

示例代码

下面是一个简单的例子,其中包括一个Clock类(发布者)和一个Display类(订阅者)。

using System;
using System.Threading;

// 定义委托(如果使用非泛型EventHandler,需要定义)
public delegate void TimerEventHandler(object sender, EventArgs e);

public class Clock
{
    // 基于委托定义事件
    public event TimerEventHandler Timer;

    // 触发事件的方法
    public void RunClock()
    {
        while (true)
        {
            Thread.Sleep(1000);
            // 触发事件
            Timer?.Invoke(this, EventArgs.Empty);
        }
    }
}

public class Display
{
    // 事件处理方法
    public void ShowTime(object sender, EventArgs e)
    {
        Console.WriteLine($"Current time: {DateTime.Now.ToLongTimeString()}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Clock clock = new Clock();
        Display display = new Display();
        // 订阅事件
        clock.Timer += display.ShowTime;
        // 启动时钟
        clock.RunClock();
    }
}

在这个例子中,Clock类每秒触发一次Timer事件,而Display类订阅这个事件并显示当前时间。

具体应用示例:

namespace WinFormsTest
{
    public partial class frmEvent : Form
    {
        /// <summary>
        ///事件的作用
        ///希望一个类的某些成员在发生某些变化时能够被外界观测到
        ///
        /// 什么是事件?
        /// 事件是委托的实例,限定了执行时的权限,只能在事件所在类的内部执行
        /// 事件虽然是public,但是只能在类的内部被调用,因此需要在类的内部定义事件触发的方法
        /// 
        /// 
        /// 定义事件的步骤
        /// 1.定义事件的委托类型
        /// 2.在类的内部声明事件
        /// 3.声明事件
        /// </summary>

        //定义警报委托 
        public delegate void AlarmEventHandle();
        internal class Alarm
        {
            public event AlarmEventHandle AlarmRaised; //在类的内部声明了一个事件
            public void ShowAlarm()
            {
                MessageBox.Show("1.警报响起!");
                Thread.Sleep(1000);
            }

            //定义事件触发的方法
            public void OnAlarmRaised()
            {
                //if (AlarmRaised != null)
                //{
                //    //AlarmRaised();
                //    AlarmRaised.Invoke();
                //}
                AlarmRaised?.Invoke();//上面if的写法等于这个
            }
        }

        public frmEvent()
        {
            InitializeComponent();
        }

        private void btnEvent_Click(object sender, EventArgs e)
        {
            Alarm alarm = new Alarm(); //事件声明,然后订阅方法
            alarm.AlarmRaised += alarm.ShowAlarm;
            alarm.AlarmRaised += new Person().processAlarm;
            alarm.AlarmRaised += () => { MessageBox.Show("3.马上处理结束...."); Thread.Sleep(1000); };
            alarm.AlarmRaised += Method1;
            //alarm.AlarmRaised -= Method1; //-=订阅移除

            alarm.OnAlarmRaised();//触发事件
        }
        void Method1()
        {
            MessageBox.Show("4.处理完成!");
            Thread.Sleep(1000);
        }

        public class Person
        {
            public void processAlarm()
            {
                MessageBox.Show("2.正在处理中.....");
                Thread.Sleep(1000);
            }
        }
    }
}

3.回调函数(callback)

回调函数用于在某个操作完成后执行特定的行为或通知。回调通常通过委托(Delegate)来实现,委托是C#中用于封装方法的对象。

委托(Delegate): 回调函数在C#中通常使用委托来表示。委托是一个类型安全的函数指针。

public delegate void MyCallback(string message);

异步回调: 在异步编程模型中,可以使用回调函数来处理异步操作的完成。

事件: 事件其实就是一种特殊类型的回调,它允许多个订阅者监听某个行为。

示例

以下是一个简单的回调函数示例:

using System;
public delegate void MyCallback(string message);  // 定义委托
public class Operation
{
    public void Execute(MyCallback callback)
    {
        // 执行某些操作...
        // ...

        // 操作完成后,调用回调函数
        callback("Operation completed!");
    }
}
//程序入口
public class Program
{
    public static void Main()
    {
        Operation operation = new Operation();
        // 定义回调函数
        MyCallback callback = (message) =>
        {
            Console.WriteLine($"Callback invoked: {message}");
        };
        // 执行操作并传入回调函数
        operation.Execute(callback);
    }
}

在这个例子中,Operation 类有一个 Execute 方法,该方法接受一个回调函数(MyCallback 委托的实例)。当 Execute 方法的操作完成后,它会调用这个回调函数。

4.匿名函数和lambda表达式

匿名函数: 匿名函数是没有名称、只有函数体的函数。在C#中,你可以使用delegate关键字来创建匿名函数。

delegate(int x) { return x * x; }

Lambda表达式: 是匿名函数的一种更简洁的表示方式,使用=>符号来分隔参数和函数体。

x => x * x

参数类型推断: Lambda表达式通常可以推断参数类型,因此你不必显式声明它。

(x, y) => x + y

多语句Lambda: 使用花括号{},你可以在Lambda表达式中编写多条语句。

x => { Console.WriteLine(x); return x * x; }

捕获外部变量: 匿名函数和Lambda表达式可以捕获其作用域内的外部变量。

int y = 10;
Func<int, int> addY = x => x + y;

应用场景: 匿名函数和Lambda表达式常用于事件订阅、LINQ查询、异步编程等。

示例代码

使用匿名函数和Lambda表达式来订阅一个事件。

using System;

public class MyEventPublisher
{
    public event Action<string> MyEvent;

    public void RaiseEvent(string message)
    {
        MyEvent?.Invoke(message);
    }
}

public class Program
{
    public static void Main()
    {
        MyEventPublisher publisher = new MyEventPublisher();

        // 使用匿名函数订阅事件
        publisher.MyEvent += delegate(string message)
        {
            Console.WriteLine($"Received message using anonymous method: {message}");
        };

        // 使用Lambda表达式订阅事件
        publisher.MyEvent += message => Console.WriteLine($"Received message using Lambda: {message}");

        // 触发事件
        publisher.RaiseEvent("Hello, World!");
    }
}

精彩推荐:
【C#进阶一】C#中的数组(Array)、集合(ArrayList,Queue,Stack, HashList)、List<T>、字典(Dictionary<K,T>)和双向链表LinkedList
【C#进阶八】C#中的序列化与反序列化下(二进制序列化、XML序列化及JSON序列化)

【C#进阶】C#语法中一些常用知识点总结
【WinForm详细教程一】WinForm中的窗体、Label、TextBox及Button控件、RadioButton和CheckBox、ListBox
【WinForm详细教程三】WinForm中的NumericUpDown、PictureBox、RichTextBox及三种Timer控件

希望有所帮助,同时欢迎关注我,后面将更新更多相关内容!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐