本文作者:小黑黑

委托和事件

小黑黑 11个月前 ( 05-24 ) 1097 抢沙发
委托和事件摘要: 一、什么是委托       委托是一个类,它定义了方法的类型,使得可以将方法当做另一个方法的参数来进行传递。二、委托的简...

一、什么是委托

       委托是一个类,它定义了方法的类型,使得可以将方法当做另一个方法的参数来进行传递。

二、委托的简单使用

       一个委托类型定义了该类型的实例能调用的一类方法,这些方法含有同样的返回值和参数(类型和个数相同)。委托和接口一样,可以定义在类的外部。如下定义了一个委托Show

public delegate int Show(int x);

此委托适用于返回值类型为int,并且有一个int类型参数的方法。如:

public static int Add(int x)
{
    return x * 2;
}

创建一个委托实例,将方法赋值给该委托,并进行调用

Show s = new Show(Add);
int result = s(2);

完整代码如下:

public delegate int Show(int x);

class Program
{
    public static int Add(int x)
    {
        return x * 2;
    }

    static void Main(string[] args)
    {
        {
            Show show = new Show(Add);
            show(2);
        }
    }
}

三、泛型委托

      如果你知道泛型,那么就很容易理解泛型委托,说白了就是含有泛型参数的委托,例如:

public delegate void Print<T>(T arg);

class Program
{


    public static void PrintString(string message)
    {
        Console.WriteLine(message);
    }
    
    static void Main(string[] args)
    {
        Print<string> print = new Print<string>(PrintString);
        print("Hello C#");
    }
}

四、Action和Func

      Action 是无返回值的委托

      Action 表示无参数,无返回值的委托

      Action<int, string> 表示有传入int,string参数,无返回值的委托

      Action 至少0个参数,最多16个参数,无返回值

static void Main(string[] args)
{
    
    //参数类型为string,无返回值的委托
    Action<string> printAction = PrintString;
    printAction("Hello C#");
}

public static void PrintString(string message)
{
    Console.WriteLine(message);
}

      Func 是有返回值的委托

      Func<int> 表示无参数,返回值为int的委托

      Func<int, int> 表示传入int参数,返回值为int的委托

      Func 最少0个参数,最多16个参数,根据返回值泛型返回,不可为void,Func中最后一个类型为返回值类型

static void Main(string[] args)
{
    //参数类型为int,返回值类型为int的委托
    Func<int, int> addFunc = Add;
    int result = addFunc(2);
}

public static int Add(int x)
{
    return x * 2;
}


五、多播委托(MuticastDelegate)

       多播委托继承自Delegate,表示多路广播委托;即,其调用列表中可以拥有多个元素的委托上,实际上,我们自定义的委托的基类就是MuticaseDelegate,所以,所有的委托实例都有多播的功能。多播委托具有一个带有链接的委托列表,称为调用列表,在对委托实例进行调用的时候,将按列表中的委托顺序进行同步调用。

class Program
{

    public void DoSomething()
    {
        Console.WriteLine("This is DoSomething");
    }

    public static void DoSomethingStatic()
    {
        Console.WriteLine("This is DoSomethingStatic");
    }
}

public class Student
{
    public void Study()
    {
        Console.WriteLine("学生爱学习");
    }
}

class Program
{
    /// <summary>
    /// 多播委托
    /// </summary>
    public void ShowMuticastDelegate()
    {
        Action action = DoSomething;
        action += DoSomething;
        action += DoSomethingStatic;
        action += new Student().Study;
        action += () => { Console.WriteLine("1243"); };

        action.Invoke();

        Console.WriteLine("*********************");

        action -= DoSomething;
        action -= DoSomethingStatic;
        action -= new Student().Study;
        action -= () => { Console.WriteLine("1243"); };
        action.Invoke();
    }
    
    static void Main(string[] args)
    {
        Program program = new Program();
        program.ShowMuticastDelegate();
    }
}

多播委托使用 "+=" 和 "-=" 用来添加和移除方法,调用时,按照方法被添加的顺序依次执行。注意,对于委托, += 和 -= 对null是不会报错的。

五、事件

声明一个事件非常简单,只需要在声明一个委托对象时加上event关键字就行。如:

/// <summary>
/// 声明一个委托
/// </summary>
public delegate void PriceChangeHandler(decimal oldPrice, decimal newPrice);

public class Phone
{
    //声明一个事件
    public event PriceChangeHandler PriceChange;
}

事件的使用和委托一样,只是多了些约束。下面是一个简单的事件的例子:

/// <summary>
/// 声明一个委托
/// </summary>
public delegate void PriceChangeHandler(decimal oldPrice, decimal newPrice);

public class Phone
{
    //声明一个事件
    public event PriceChangeHandler PriceChange;

    decimal price;

    public decimal Price
    {
        get { return price; }
        set
        {
            if (price == value)
            {
                return;
            }

            decimal oldPrice = price;
            price = value;
            if (PriceChange != null)
            {
                PriceChange(oldPrice, price);
            }
        }
    }
}

class Program
{

    static void Main(string[] args)
    {
        {
            Phone phone = new Phone() { Price = 5000 };

            //订阅事件
            phone.PriceChange += Phone_PriceChange;

            //调整价格,会触发事件
            phone.Price = 4000;
        }
    }
}

       运行结果,控制台将会打出 “手机大促销,现在只卖4000,原价:5000,快来抢购呦!!!” 这句话,我们可以将上面的event关键字去掉,结果还是一样的。那么委托和事件到底有何不同呢? 可以用事件的地方就一定可以用委托代替。

       但事件有一系列规则和约束泳衣保证程序的安全可控,事件只有 += 和 -= 操作,这样订阅者只能有订阅或取消订阅操作,没有权限执行其他操作。如果是委托,那么订阅者就可以使用 = 来对委托对象重新赋值(其它订阅者全部被取消订阅),甚至将其设置为null,甚至委托者还可以直接调用委托,这些操作都是很危险的操作,广播者就失去了独享控制权。而事件只有在声明它的类中使用,甚至在子类中不能对其使用 = 重新赋值和调用,所以,事件保证了程序的安全性和健壮性。

六、事件的标准模式 EventHandler

       .NET 框架为事件编程定义了一个标准模式。设定这个标准是为了让.NET框架和用户代码保持一致。System.EventArgs是标准模式的核心,它是一个没有任何成员,用于传递事件参数的基类。按照标准模式,我们对于上面的Phone6示例进行重写。首先定义EventArgs:

public class PriceChangeEventArgs : EventArgs
{
    public readonly decimal OldPrice;

    public readonly decimal NewPrice;

    public PriceChangeEventArgs(decimal oldPrice, decimal newPrice)
    {
        this.OldPrice = oldPrice;
        this.NewPrice = newPrice;
    }
}

接下来,我们修改Phone类和Program类,代码如下:

public class Phone
{

    decimal price;

    public event EventHandler<PriceChangeEventArgs> PriceChanged;

    protected virtual void OnPriceChanged(PriceChangeEventArgs e)
    {
        if (PriceChanged != null)
        {
            PriceChanged(this, e);
        }
    }

    public decimal Price
    {
        get { return price; }
        set
        {
            if (price == value)
            {
                return;
            }

            decimal oldPrice = price;
            price = value;

            if (PriceChanged != null)
            {
                OnPriceChanged(new PriceChangeEventArgs(oldPrice, price));
            }
        }
    }
  }
}

class Program
{

    static void Main(string[] args)
    {
        {
            Phone phone = new Phone() { Price = 5000 }

            //订阅事件
            phone.PriceChanged += Phone_PriceChange;

            //调整价格,会触发事件
            phone.Price = 4000;
        }
    }
    
    static void Phone_PriceChange(object sender, PriceChangeEventArgs e)
    {
        Console.WriteLine($"手机大促销,现在只卖{e.NewPrice},原价:{e.OldPrice},快来抢购呦!!!");
    }
}


分享到: 网站分享代码

发表评论

快捷回复:

评论列表 (暂无评论,1097人围观)参与讨论

还没有评论,来说两句吧...