C# 为什么DependencyProperty.UnsetValue被传递到Equals()中

C# 为什么DependencyProperty.UnsetValue被传递到Equals()中,c#,wpf,mvvm,combobox,C#,Wpf,Mvvm,Combobox,我正在开发一个简单的WPF应用程序。目前,该应用程序仅管理组合框中的视频游戏列表。游戏列表被序列化到XML文件中/从XML文件中序列化 当前被破坏的功能是从组合框中选择游戏的能力,这使其成为要管理的“活动”游戏。活动游戏将存储为应用程序设置。为了实现这一点,我使用双向数据绑定,并将组合框中的SelectedItem绑定到ViewModel中的SelectedName 当我运行应用程序并从组合框中选择一个项目时,我在Game.Equals(Game-other)方法上得到一个空引用异常。调试时,我

我正在开发一个简单的WPF应用程序。目前,该应用程序仅管理组合框中的视频游戏列表。游戏列表被序列化到XML文件中/从XML文件中序列化

当前被破坏的功能是从组合框中选择游戏的能力,这使其成为要管理的“活动”游戏。活动游戏将存储为应用程序设置。为了实现这一点,我使用双向数据绑定,并将组合框中的SelectedItem绑定到ViewModel中的SelectedName

当我运行应用程序并从组合框中选择一个项目时,我在Game.Equals(Game-other)方法上得到一个空引用异常。调试时,我看到DependencyProperty.UnsetValue作为参数传递给重写的Game.Equals(object obj)方法,该方法导致
obj as Game
将obj设为null

我不明白为什么当我所做的只是从ComboBox值中选择一个项时,首先会调用Equals,所以我猜这是WPF的固有特性。代码在转到Equals方法之前似乎没有碰到任何其他断点,因此我甚至不确定如何调试该问题。我也不明白DependencyProperty.UnsetValue是从哪里来的。我在这里完全迷失了方向,希望能有任何见解,包括如何进一步调试

编辑:正如格伦所提到的,WPF的某些底层组件必须调用Equals。至少就目前而言,我的解决方案是简单地在Equals覆盖中添加一个空检查

模型类

public class Game : IEntity, IEquatable<Game>
{
    [XmlElement("Name")]
    public string Name { get; set; }

    [XmlElement("ExecutablePath")]
    public string ExecutablePath { get; set; } 

    public Game(string name, string executablePath)
    {
        Name = name;
        ExecutablePath = executablePath;
    }

    private Game() { } // Required for XML serialization.

    public bool Equals(Game other)
    {
        return Name.EqualsIgnoreCase(other.Name);
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Game);
    }
}
公共类游戏:易学,易学
{
[XmlElement(“名称”)]
公共字符串名称{get;set;}
[XmlElement(“可执行路径”)]
公共字符串可执行路径{get;set;}
公共游戏(字符串名称、字符串可执行路径)
{
名称=名称;
可执行路径=可执行路径;
}
XML序列化需要private Game(){}//。
公共布尔等于(游戏其他)
{
返回Name.EqualsIgnoreCase(其他.Name);
}
公共覆盖布尔等于(对象对象对象)
{
回报相等(obj作为游戏);
}
}
视图模型

public class GamesViewModel
{
    // The GameRepository retrieves the game collection from the XML file.
    private readonly GameRepository _gameRepository;

    public ObservableCollection<Game> Games
    {
        get { return new ObservableCollection<Game>(_gameRepository.Items); }
    }

    public Game SelectedGame
    {
        get { return Settings.Default.ActiveGame; }
        set
        {
            if (!Settings.Default.ActiveGame.Equals(value))
            {
                Settings.Default.ActiveGame = value;
                Settings.Default.Save();
            }
        }
    }

    public GamesViewModel()
    {
        _gameRepository = RepositorySingletons.GameRepository;
    }
}
public class GamesViewModel
{
//GameRepository从XML文件检索游戏集合。
私有只读游戏存储库\u游戏存储库;
公开募捐小游戏
{
获取{returnnewobserveCollection(_gameRepository.Items);}
}
公共游戏选择名称
{
获取{return Settings.Default.ActiveGame;}
设置
{
如果(!Settings.Default.ActiveGame.Equals(value))
{
Settings.Default.ActiveGame=值;
Settings.Default.Save();
}
}
}
公共游戏可视模型()
{
_gameRepository=repositorySingleton.gameRepository;
}
}
查看

<UserControl x:Class="ENBOrganizer.UI.Views.GamesView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ENBOrganizer.UI.ViewModels" 
             mc:Ignorable="d" >
    <UserControl.DataContext>
        <local:GamesViewModel />
    </UserControl.DataContext>
    <Grid>
        <ComboBox Name="GamesComboBox" ItemsSource="{Binding Games}" SelectedItem="{Binding SelectedGame}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                        <TextBlock Text="{Binding Name}" VerticalAlignment="Center" Padding="5,0,0,0" />
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </Grid>
</UserControl>

当您的
组合框的
SelectedItem
从视图中更改时,您会发现您的源属性(
SelectedName
)可能会设置两次,一次设置值为
null
(因为不再选择上一项),然后设置一次新选择的项

如果您有依赖于对象不为null的代码,通常处理这种情况的方法是使用简单的null值检查:

if (value != null)
{
    // Code that relies on value not being null
}

在这一点上,我没有专门依赖于空检查的代码。我可能需要在未来,但为了早期开发的目的,我只有2个游戏的组合框,所以我的选择将永远不会为空。你知道为什么当我所做的只是在组合框中选择一个项目时会调用Equals方法吗?这是我的第一个猜测。我在SelectedName属性的setter上设置了一个断点,但是Equals方法在setter被命中之前抛出了一个异常。Object.Equals在.NET后台用于许多不同的事情,重写该方法时应该非常小心。看,你的Equals方法相当懒惰。你应该能够在不抛出NRE的情况下处理这个问题。我接受空检查。我只是想了解为什么要首先传入null。