C# 基础语法 06 委托事件

简单复习一下 C# 基础语法(第六期)。

委托

委托是一个类,可以被实例化,包装一系列的方法,相当于一个方法的容器,可以作为参数传入其他方法。

模板

方法的参数列表内,有一个委托参数,方法体会调用这个委托参数。

而委托参数包装的是什么样的方法,则由外部传入的方法决定。

相当于一个填空题,借助外部的方法产生结果。

模板委托方法通常在代码的中间部分,有返回值。

定义一个 Person 类,包含人物的名字和正在思考的内容。

同时,定义一个 Answer 类,用来包装人物答题的内容。

public class Person
{
    public string person;
    public string result;

    public Person(string name, string thinking)
    {
        person = name;
        result = thinking;
    }

    public Answer Think()
    {
        Answer answer = new Answer();
        answer.person = person;
        answer.result = result;
        return answer;
    }
}

public class Answer
{
    public string person;
    public string result;
}

再定义一个 Exam 类,输出答题人和答案。

其中,方法的参数是一个委托,Func 类是 C# 内部定义好的一个委托类,它可以有返回值,尖括号 < > 内填入返回值的类型。

在方法内部调用委托,得到 Answer 类型的返回值,把内容打印输出。

public class Exam
{
    public void AnswerQuestion(Func<Answer> solution)
    {
        Answer answer = solution.Invoke();
        Console.WriteLine($"答题人:{answer.person} 答案:{answer.result}");
    }
}

实际使用时,可以定义 Func 变量,new 一个委托实例,将一个实例的方法作为参数传入。

然后把委托变量传给 AnswerQuestion 方法,同一个方法,可以接收不同的委托变量,实现功能的复用。

public class Program
{
    static void Main()
    {
        Exam exam = new Exam();
        Person person1 = new Person("小明", "鸡排");
        Person person2 = new Person("小红", "奶茶");

        Func<Answer> func1 = new Func<Answer>(person1.Think);
        Func<Answer> func2 = new Func<Answer>(person2.Think);

        exam.AnswerQuestion(func1);
        exam.AnswerQuestion(func2);
        
        // 答题人:小明 答案:鸡排
        // 答题人:小红 答案:奶茶
    }
}

回调

同样,方法的参数列表内,有一个委托参数。

在执行完一系列代码之后,调用外部方法进行回调。

回调委托方法通常在代码的末尾部分,无返回值。

继续上面的例子,添加一个 Logger 类,用于答题后的信息提示。

public class Logger
{
    public string message;

    public Logger(string message)
    {
        this.message = message;
    }

    public void Log() 
    {
        Console.WriteLine(message);
    }
}

扩展 AnswerQuestion 的参数列表,并在打印答题结果之后进行回调。

Action 类也是 C# 内部定义好的一个委托类,无返回值。

public class Exam
{
    public void AnswerQuestion(Func<Answer> solution, Action callback)
    {
        Answer answer = solution.Invoke();
        Console.WriteLine($"答题人:{answer.person} 答案:{answer.result}");
        callback.Invoke();
    }
}

实例化 Logger 类,填入信息提示。

可以定义 Action 变量并实例化,传入 Logger 类的 Log 方法。

然后把 action 变量作为参数,作为回调方法传入 AnswerQuestion 方法中。

现在每次打印答题结果之后,都会有答题成功的信息提示。

public class Program
{
    static void Main()
    {
        Exam exam = new Exam();
        Person person1 = new Person("小明", "鸡排");
        Person person2 = new Person("小红", "奶茶");

        Func<Answer> func1 = new Func<Answer>(person1.Think);
        Func<Answer> func2 = new Func<Answer>(person2.Think);

        Logger logger = new Logger("答题成功");
        Action action = new Action(logger.Log);

        exam.AnswerQuestion(func1, action);
        exam.AnswerQuestion(func2, action);
        
        // 答题人:小明 答案:鸡排
        // 答题成功
        // 答题人:小红 答案:奶茶
        // 答题成功
    }
}

多播

委托是一对多的,也就是说,可以包装多个方法,当委托被调用时,会有多个方法被同时调用,调用的顺序则是方法被填入委托变量时的顺序。

在上述的例子进行扩展,再实例化一个 Logger,会提示【请继续答题】,并且使用 += 符号给 action 变量再添加一个方法引用。

此时回调方法会输出两行。

public class Program
{
    static void Main()
    {
        // ...

        Logger logger1 = new Logger("答题成功");
        Logger logger2 = new Logger("请继续答题");
        Action action = new Action(logger1.Log);
        action += logger2.Log;

        exam.AnswerQuestion(func1, action);
        exam.AnswerQuestion(func2, action);
        
        // 答题人:小明 答案:鸡排
        // 答题成功
        // 请继续答题
        // 答题人:小红 答案:奶茶
        // 答题成功
        // 请继续答题
    }
}

事件

事件是在委托的基础上,对委托的访问进行限制,事件的右侧只能是 += 或 -= 符号,不能被 = 符号直接覆盖,也不能被 .Invoke 调用。

接着上述的例子,自定义一个 SubmitEventHandler 委托,和一个 SubmitSystem 类。

SubmitSystem 类包含一个委托字段,和一个事件。

事件必须同时包含 add 和 remove,对委托字段进行添加方法和移除方法。

同时,因为委托字段是私有的,外部无法调用,事件也不能被调用,所以提供一个方法 TriggerSubmit 对委托字段进行调用。

注意:如果 submitEventHandler 没有包含任何方法,则会是 null,需要进行判空。

public delegate void SubmitEventHandler();

public class SubmitSystem
{
    private SubmitEventHandler submitEventHandler;

    public event SubmitEventHandler OnSubmit
    {
        add
        {
            submitEventHandler += value;
        }
        remove
        {
            submitEventHandler -= value;
        }
    }

    public void TriggerSubmit()
    {
        if (submitEventHandler != null)
        {
            submitEventHandler.Invoke();
        }
    }
}

现在,继续添加主程序的逻辑。

实例化 submitSystem 变量,给它的事件添加两个方法,并通过 TriggerSubmit 调用被事件保护起来的私有委托。

public class Program
{
    static void Main()
    {
        // ...
        
        Logger logger3 = new Logger("小明提交了试卷");
        Logger logger4 = new Logger("小红提交了试卷");
        
        SubmitSystem submitSystem = new SubmitSystem();
        submitSystem.OnSubmit += logger3.Log;
        submitSystem.OnSubmit += logger4.Log;
        
        submitSystem.TriggerSubmit();
        
        // ...
        // 小明提交了试卷
        // 小红提交了试卷
    }
}

上述事件的定义是一个完整定义,实际上可以简化。

可以直接定义事件,无需定义私有委托字段,也无需为事件添加 add 和 remove 访问器。

public delegate void SubmitEventHandler();

public class SubmitSystem
{
    public event SubmitEventHandler OnSubmit;

    public void TriggerSubmit()
    {
        if (OnSubmit != null)
        {
            OnSubmit.Invoke();
        }
    }
}

此时,事件的右侧似乎可以被 .Invoke 调用,还可以判空。

实际上,因为 C# 会在编译时自动生成私有委托字段,但是编写代码时并没有私有委托字段,所以不得不使用事件来触发调用,不过外部对事件的访问依然只能是 += 和 -= 符号。

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值