WPF上位机开发第五课_2025-04-12

WPF上位机开发之第五课->委托

一.认识委托->不打不相识

菜鸟入行的时候,犯了一个错误,跨线程更新UI界面。

遇到问题怎么办呢?当然是上网一顿搜索,搜索完找到了解决方案:

报错原因:

  • 在Windows Forms中,UI控件只能在创建它们的线程(主线程/UI线程)中访问和修改

  • 当我们在Task.Run中执行代码时,代码是在后台线程中运行的

  • 直接修改UI控件(如label1.Text)会导致跨线程访问异常

Task.Run(() => 
            {
                try
                {
                    for (int i = 0; i < 10; i++)
                    {
                        // Simulate some work
                        System.Threading.Thread.Sleep(1000);

                        //label1.Text = $"Task running... {i + 1}/10";

                        //方法一: Update the UI By Winform
                        this.Invoke((MethodInvoker)delegate
                        {
                            label1.Text = $"Task running... {i + 1}/10";
                        });
                        // 方法二:Update the UI using Invoke
                        this.Invoke(new Action(() => { label1.Text = $"Task running... {i + 1}/10"; }));
                    }
                }
                catch (Exception ex)
                {
                    // Handle exceptions
                    MessageBox.Show($"An error occurred: {ex.Message}");
                }
                
            } );
  • 第一种使用MethodInvoker委托,这是Windows Forms专门提供的一个无参数无返回值的委托类型

  • 第二种使用Action委托,这是.NET Framework提供的通用委托类型

  • 两种方式都能达到相同的目的,但MethodInvoker是专门为Windows Forms设计的,语义上更明确

二.面试中的委托

后来参加面试的时候,总是会遇到这样的对话:谈谈你对委托的认识。

太理论的我也讲不上来,主要是一下几点吧

1.委托是class,我反编译后查到委托就是class,所以我们在定义委托的时候,需要跟class平级。

2.委托相当于C/C++里的函数指针

3.说说我平时用到委托的地方

A.跨线程修改界面控件的值

B.回调函数->常用于取相机拍照产生的图像数据

不使用Action,Func的版本

public class DataProcessor
{
    // 定义回调委托
    public delegate void ProcessingCallback(string result);

    public void ProcessData(string data, ProcessingCallback callback)
    {
        // 模拟耗时操作
        Thread.Sleep(1000);
        
        // 调用回调
        callback($"Processed: {data}");
    }
}

class Program
{
    static void Main()
    {
        var processor = new DataProcessor();
        
        // 使用回调
        processor.ProcessData("Hello", result => 
        {
            Console.WriteLine(result);
        });
    }
}

C.把函数单座参数进行传递

  • 看Task
[__DynamicallyInvokable]
public static Task Run(Action action)
{
    StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
    return InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default, TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
}
  • 看Thread
[SecuritySafeCritical]
public Thread(ThreadStart start)
{
    if (start == null)
    {
        throw new ArgumentNullException("start");
    }

    SetStartHelper(start, 0);
}
  • 再看ThreadStart
[ComVisible(true)]
public delegate void ThreadStart();

D…NET内置了Action和Func两个委托,一个是不带返回值的,一个是带返回值的,他们最多可以有16个参数。

特性ActionFunc
定义无返回值的委托有返回值的委托
返回值
参数可有0个或多个输入参数可有0个或多个输入参数,但必须有返回值
用途执行操作,如事件处理、回调执行计算或查询,返回结果
是否可以作为Lambda表达式
是否可以作为匿名方法
是否可以作为方法组
是否可以作为事件处理器不适合(事件处理器通常无返回值)
是否可以作为方法参数
是否可以作为方法返回值不适合(无返回值)
是否可以作为异步方法的返回值不适合可以(返回Task<T>

E.Linq查询:用于数据查询和转换

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        
        // 使用委托进行过滤
        var evenNumbers = numbers.Where(n => n % 2 == 0);
        
        // 使用委托进行转换
        var squares = numbers.Select(n => n * n);
        
        // 使用委托进行排序
        var sorted = numbers.OrderBy(n => n);
        
        Console.WriteLine("Even numbers: " + string.Join(", ", evenNumbers));
        Console.WriteLine("Squares: " + string.Join(", ", squares));
        Console.WriteLine("Sorted: " + string.Join(", ", sorted));
    }
}

三.Net中的其他委托

主要的内置委托类型总结:

1.Action系列

  • Action:无参数无返回值

  • Action:一个参数无返回值

  • Action<T1, T2>:两个参数无返回值

  • 最多支持16个参数

2.Func系列

  • Func:无参数有返回值

  • Func<T, TResult>:一个参数有返回值

  • Func<T1, T2, TResult>:两个参数有返回值

  • 最多支持16个参数

3.Predicate

  • 接受一个参数,返回bool

  • 常用于条件判断

  • 例如:List.FindAll(Predicate)

4.Comparison

  • 接受两个参数,返回int

  • 用于比较两个对象

  • 例如:List.Sort(Comparison)

5.Converter<TInput, TOutput>

  • 接受一个输入类型,返回输出类型

  • 用于类型转换

  • 例如:List.ConvertAll(Converter<T, TOutput>)

6/EventHandler

  • 用于处理无参数事件

  • 签名:void (object sender, EventArgs e)

7.EventHandler

  • 用于处理带参数的事件

  • 签名:void (object sender, TEventArgs e)

这些内置委托类型覆盖了大多数常见的委托使用场景。选择使用哪种委托主要取决于:

  • 是否需要返回值

  • 参数的个数

  • 参数的类型

  • 具体的应用场景

在实际开发中,这些内置委托类型可以大大简化代码,避免重复定义相似的委托类型。

四Action的一些示例

1.基本示例

class Program
{
    static void Main()
    {
        // 无参数Action
        Action sayHello = () => Console.WriteLine("Hello!");
        sayHello(); // 输出: Hello!

        // 一个参数Action
        Action<string> greet = name => Console.WriteLine($"Hello, {name}!");
        greet("Alice"); // 输出: Hello, Alice!

        // 两个参数Action
        Action<string, int> printInfo = (name, age) => 
            Console.WriteLine($"{name} is {age} years old.");
        printInfo("Bob", 30); // 输出: Bob is 30 years old.

        // 三个参数Action
        Action<string, int, string> printDetails = (name, age, city) =>
            Console.WriteLine($"{name}, {age} years old, lives in {city}");
        printDetails("Charlie", 25, "New York"); // 输出: Charlie, 25 years old, lives in New York
    }
}

2.1回调函数Lambda版本

class DataProcessor
{
    // 使用Action作为回调,不需要返回值
    public void ProcessData(string data, Action<string> callback)
    {
        Console.WriteLine("Processing data...");
        Thread.Sleep(1000); // 模拟耗时操作
        
        // 处理完成后调用回调
        string result = $"Processed: {data}";
        callback(result);
    }
}

class Program
{
    static void Main()
    {
        var processor = new DataProcessor();
        
        // 示例1:简单的回调
        processor.ProcessData("Hello", result => 
        {
            Console.WriteLine($"Callback received: {result}");
        });

        // 示例2:更新UI的回调
        processor.ProcessData("UI Update", result =>
        {
            // 假设这是在Windows Forms应用中
            // this.Invoke(() => label1.Text = result);
            Console.WriteLine($"UI Updated: {result}");
        });

        // 示例3:多步骤处理
        processor.ProcessData("Multi-step", result =>
        {
            Console.WriteLine($"Step 1: {result}");
            processor.ProcessData(result, secondResult =>
            {
                Console.WriteLine($"Step 2: {secondResult}");
            });
        });
    }
}

2.2回调函数不用Lambda版本

class DataProcessor
{
    // 使用Action作为回调,不需要返回值
    public void ProcessData(string data, Action<string> callback)
    {
        Console.WriteLine("Processing data...");
        Thread.Sleep(1000); // 模拟耗时操作
        
        // 处理完成后调用回调
        string result = $"Processed: {data}";
        callback(result);
    }
}

class Program
{
    // 定义回调方法
    static void SimpleCallback(string result)
    {
        Console.WriteLine($"Callback received: {result}");
    }

    // 定义UI更新回调方法
    static void UpdateUICallback(string result)
    {
        // 假设这是在Windows Forms应用中
        // this.Invoke(() => label1.Text = result);
        Console.WriteLine($"UI Updated: {result}");
    }

    // 定义多步骤处理的回调方法
    static void MultiStepCallback(string result, DataProcessor processor)
    {
        Console.WriteLine($"Step 1: {result}");
        processor.ProcessData(result, SecondStepCallback);
    }

    // 定义第二步的回调方法
    static void SecondStepCallback(string result)
    {
        Console.WriteLine($"Step 2: {result}");
    }

    static void Main()
    {
        var processor = new DataProcessor();
        
        // 示例1:简单的回调
        processor.ProcessData("Hello", SimpleCallback);

        // 示例2:更新UI的回调
        processor.ProcessData("UI Update", UpdateUICallback);

        // 示例3:多步骤处理
        processor.ProcessData("Multi-step", result => MultiStepCallback(result, processor));
    }
}

3.实现观察者模式

// 主题(被观察者)
class Subject
{
    private string state;
    private List<Action<string>> observers = new List<Action<string>>();

    public string State
    {
        get => state;
        set
        {
            state = value;
            NotifyObservers();
        }
    }

    // 注册观察者
    public void Attach(Action<string> observer)
    {
        observers.Add(observer);
    }

    // 移除观察者
    public void Detach(Action<string> observer)
    {
        observers.Remove(observer);
    }

    // 通知所有观察者
    private void NotifyObservers()
    {
        foreach (var observer in observers)
        {
            observer(state);
        }
    }
}

// 观察者
class Observer
{
    private string name;

    public Observer(string name)
    {
        this.name = name;
    }

    // 观察者的更新方法
    public void Update(string state)
    {
        Console.WriteLine($"{name} received update: {state}");
    }
}

class Program
{
    static void Main()
    {
        // 创建主题
        var subject = new Subject();

        // 创建观察者
        var observer1 = new Observer("Observer 1");
        var observer2 = new Observer("Observer 2");

        // 注册观察者
        subject.Attach(observer1.Update);
        subject.Attach(observer2.Update);

        // 改变主题状态,观察者会收到通知
        subject.State = "State 1";
        subject.State = "State 2";

        // 移除一个观察者
        subject.Detach(observer1.Update);

        // 再次改变状态,只有observer2会收到通知
        subject.State = "State 3";
    }
}

五Func的一些示例

1.基础示例

class Program
{
    static void Main()
    {
        // Func<T, TResult> - 接受一个参数,返回一个结果
        Func<int, int> square = x => x * x;
        Console.WriteLine($"Square of 5: {square(5)}"); // 输出: 25

        // Func<T1, T2, TResult> - 接受两个参数,返回一个结果
        Func<int, int, int> add = (x, y) => x + y;
        Console.WriteLine($"Sum of 5 and 3: {add(5, 3)}"); // 输出: 8

        // Func<T1, T2, T3, TResult> - 接受三个参数,返回一个结果
        Func<int, int, int, int> multiply = (x, y, z) => x * y * z;
        Console.WriteLine($"Product of 2, 3, 4: {multiply(2, 3, 4)}"); // 输出: 24
    }
}

2.回调函数

class Calculator
{
    // 使用Func作为回调,需要返回值
    public int Calculate(int a, int b, Func<int, int, int> operation)
    {
        Console.WriteLine($"Calculating with {a} and {b}");
        return operation(a, b);
    }

    // 使用Func作为回调,返回处理后的数据
    public T ProcessData<T>(T data, Func<T, T> transform)
    {
        Console.WriteLine("Processing data...");
        Thread.Sleep(1000); // 模拟耗时操作
        return transform(data);
    }
}

class Program
{
    static void Main()
    {
        var calculator = new Calculator();

        // 示例1:基本计算回调
        int sum = calculator.Calculate(5, 3, (x, y) => x + y);
        Console.WriteLine($"Sum: {sum}");

        int product = calculator.Calculate(5, 3, (x, y) => x * y);
        Console.WriteLine($"Product: {product}");

        // 示例2:数据处理回调
        string result = calculator.ProcessData("hello", str => str.ToUpper());
        Console.WriteLine($"Uppercase: {result}");

        // 示例3:复杂数据处理
        var numbers = new List<int> { 1, 2, 3, 4, 5 };
        var processed = calculator.ProcessData(numbers, list =>
        {
            return list.Select(x => x * 2)
                      .Where(x => x > 5)
                      .ToList();
        });
        Console.WriteLine($"Processed numbers: {string.Join(", ", processed)}");
    }
}

3.在linq中的使用

class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice", Age = 25 },
            new Person { Name = "Bob", Age = 30 },
            new Person { Name = "Charlie", Age = 35 }
        };

        // 使用Func进行筛选
        Func<Person, bool> isAdult = p => p.Age >= 18;
        var adults = people.Where(isAdult);

        // 使用Func进行转换
        Func<Person, string> getName = p => p.Name;
        var names = people.Select(getName);

        // 使用Func进行排序
        Func<Person, int> getAge = p => p.Age;
        var sortedByAge = people.OrderBy(getAge);

        Console.WriteLine("Adults:");
        foreach (var person in adults)
        {
            Console.WriteLine($"{person.Name} - {person.Age}");
        }
    }
}

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

4.实现观察者模式

// 主题(被观察者)
class Subject
{
    private string state;
    private List<Func<string, bool>> observers = new List<Func<string, bool>>();

    public string State
    {
        get => state;
        set
        {
            state = value;
            NotifyObservers();
        }
    }

    // 注册观察者
    public void Attach(Func<string, bool> observer)
    {
        observers.Add(observer);
    }

    // 移除观察者
    public void Detach(Func<string, bool> observer)
    {
        observers.Remove(observer);
    }

    // 通知所有观察者
    private void NotifyObservers()
    {
        foreach (var observer in observers)
        {
            bool success = observer(state);
            if (!success)
            {
                Console.WriteLine("Observer failed to process update");
            }
        }
    }
}

// 观察者
class Observer
{
    private string name;
    private int maxUpdates;
    private int updateCount;

    public Observer(string name, int maxUpdates)
    {
        this.name = name;
        this.maxUpdates = maxUpdates;
        this.updateCount = 0;
    }

    // 观察者的更新方法,返回是否成功处理
    public bool Update(string state)
    {
        if (updateCount >= maxUpdates)
        {
            Console.WriteLine($"{name} has reached maximum updates");
            return false;
        }

        updateCount++;
        Console.WriteLine($"{name} received update {updateCount}: {state}");
        return true;
    }
}

class Program
{
    static void Main()
    {
        // 创建主题
        var subject = new Subject();

        // 创建观察者
        var observer1 = new Observer("Observer 1", 2);  // 最多处理2次更新
        var observer2 = new Observer("Observer 2", 3);  // 最多处理3次更新

        // 注册观察者
        subject.Attach(observer1.Update);
        subject.Attach(observer2.Update);

        // 改变主题状态,观察者会收到通知
        subject.State = "State 1";
        subject.State = "State 2";
        subject.State = "State 3";  // observer1将不再处理
        subject.State = "State 4";  // observer1和observer2都不再处理
    }
}

完结,撒花!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WPF工业上位机

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值