C#事件:定义:如何可以为空?

C#事件:定义:如何可以为空?,c#,event-handling,undefined,C#,Event Handling,Undefined,在一篇关于将MVVM与Xamarin.Forms结合使用的C#教程中,我在MVVM类中偶然发现了以下序列: public event PropertyChangedEventHandler PropertyChanged; void OnPropertyChanged(string PropertyName) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(PropertyName));

在一篇关于将MVVM与Xamarin.Forms结合使用的C#教程中,我在MVVM类中偶然发现了以下序列:

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(string PropertyName) {
        PropertyChanged.Invoke(this, new PropertyChangedEventArgs(PropertyName));
    }
作者建议对PropertyChanged执行空检查,并调用它:

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
在哪些情况下,PropertyChanged可以为空?我的意思是,它不仅在上面声明了一行,不声明它不会让代码编译,那么PropertyChanged怎么可能是空的呢?

通常,其他类会在声明
公共事件的类上注册它们的处理程序,无论什么
——只要没有类在您的
PropertyChanged
命名事件处理程序上注册它的处理程序,它实际上就是
空的

因此,建议使用

PropertyChanged?.Invoke(...)
以避免获取NullRef异常

MSDN文档:

读取事件处理:


代码/用法:

using System;
using System.ComponentModel; 

public class Program
{
    public static void Main(string[] args)
    {
        C1 c1 = new C1();  // create instance of class that raises event 
        L1 l1 = new L1();  // create instance of 1st class that handles event
        L2 l2 = new L2();  // create instande of 2nd class that handles event

        // no handlers are yet registered on the event

        try
        {
            c1.Raise(); // will crash and output exception as no handler set yet
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

        c1.SafeRaise(); // safe, will print message

        l1.AddListener(c1); // add as listener 
        c1.Raise();         // prints handled message

        l2.AddListener(c1); // add 2nd listener
        c1.Raise();         // both print handled message

        Console.ReadLine(); // stop console from closing
    }

    // Has the prop-changed and 2 raise-methods, 1 safe, 1 not.
    public class C1 : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        // unsafe - will throw if not yet any listerer registered
        public void Raise() => 
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(
                DateTime.Now.ToString()));

        // safe, even if no listener registered.
        public void SafeRaise()
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(
                DateTime.Now.ToString()));
            Console.WriteLine("PropertyHandlerNotSet! - but safe due to '"+
                              " PropertyChanged?.Invoke(this, new PropertyChanged"+
                              "EventArgs(DateTime.Now.ToString()));' ");
        }
    }

    public class L1
    {
        public void AddListener(C1 c) => c.PropertyChanged += this.Handler;

        public void Handler(object sender, PropertyChangedEventArgs e) 
            => Console.WriteLine("L1: " + e.PropertyName);
    }

    public class L2
    {
        public void AddListener(C1 c) => c.PropertyChanged += this.Handler;

        public void Handler(object sender, PropertyChangedEventArgs e) 
            => Console.WriteLine("L2: " + e.PropertyName);
    }
}
输出:

Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
PropertyHandlerNotSet! - but safe due to ' PropertyChanged?.Invoke(this, new Pro
pertyChangedEventArgs(DateTime.Now.ToString()));'
L1: 06.03.2018 23:03:50                   // 1st raise after registering
L1: 06.03.2018 23:03:50                   // 2nd raise after registering
L2: 06.03.2018 23:03:50

如果看不到全部范围,很难判断。然而,这里有两种可能性:

它被公开

PropertyChanged
标记为
public
,这意味着外部参与者可以将其设置为其他值,包括null。即使成员是私有的,内部方法也可能无法设置该值

未分配它


在代码段中,您不指示是否为变量赋值。如果如图所示执行,它将为空。

声明它并不意味着分配它。空检查是所有事件(委托)的一般准则。在您自己的示例中为空!你试过了吗?我想没有。正如@DourHighArch所说的,你已经在你的帖子里。。。你能澄清一下为什么你认为帖子中的代码没有显示“在什么情况下可以将财产更改为空”@Dour&Alexei:我在问之前试过了,不管有没有检查,都有效,相信我。该代码是Xamarin表单项目的一部分。对不起,我没有提到这一点,因为我认为这不相关。据我所知,在Xamarin.Forms项目中,这个事件总是由Xamarin.Forms库订阅的,因此在Xamarin生态系统中它永远不会为空。然而,检查是一种良好的做法。我会相应地修改我的问题文本。好的,我想我明白了。我对如何声明和调用事件处理程序的看法刚刚转变了180度:-),以确定我是否真的理解了它:。。。如果此代码与Xamarin.Forms库链接,该库具有PropertyChangedEventHandler事件的处理程序,那么PropertyChanged是否会获得非空值?如果是-如果多个库具有PropertyChangedEventHandler怎么办?编译器会拒绝这个,还是所有的处理程序都会尝试激活?@Nimral-请参阅代码。@Nimral我不知道Xamarin,现在主要做WPF。您可以注册多个处理程序,并以无序方式“同时”通知所有处理程序。所有人都可以根据自己的意愿处理引发的事件。声明后,事件可以由我自己的代码调用,并通知Xamarin.Forms绑定成员已更改了值。然后,Xamarin将更新屏幕上的值。我还没有深入研究事件处理程序,现在我已经阅读了关于“委托模型”和“观察者模式”的基础知识,这些东西都很到位。