gpt4 book ai didi

c# - INotifyPropertyChanged 奇怪的 NullReferenceException

转载 作者:太空宇宙 更新时间:2023-11-03 21:31:58 27 4
gpt4 key购买 nike

我有一个基类实现了我所有 View 模型继承自的 INotifyPropertyChanged:

public class BaseChangeNotify : INotifyPropertyChanged
{
private bool isDirty;

public BaseChangeNotify()
{
}

public event PropertyChangedEventHandler PropertyChanged;

public bool IsDirty
{
get
{
return this.isDirty;
}

set
{
this.isDirty = value;
this.OnPropertyChanged();
}
}

public virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
// Perform the IsDirty check so we don't get stuck in a infinite loop.
if (propertyName != "IsDirty")
{
this.IsDirty = true; // Each time a property value is changed, we set the dirty bool.
}

if (this.PropertyChanged != null)
{
// Invoke the event handlers attached by other objects.
try
{
Application.Current.Dispatcher.Invoke(() =>
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)));
}
catch (Exception exception)
{
throw exception;
}
}
}
}

我有一个主视图模型,实例 subview 模型包装了数据库中的模型。我注册为 subview 模型 PropertyChanged 事件的监听器,这样我可以在更改 subview 模型时使主视图模型“变脏”。然而,问题是当 child 被更改时,我得到一个与父 View 模型关联的空引用异常。

主视图模型(简化):

public class DiaryDescriptionViewModel : BaseViewModel, IDataErrorInfo
{
private Diary diary;

private ObservableCollection<DiaryDescriptionDetailsViewModel> diaryDescriptions;

private DiaryDescriptionDetailsViewModel selectedDiaryDescription;

private List<SectionViewModel> projectSections;

public DiaryDescriptionViewModel()
{
}

public DiaryDescriptionViewModel(Diary diary, UserViewModel user) : base(user)
{
this.diary = diary;

// Restore any previously saved descriptions.
var diaryRepository = new DiaryRepository();
List<DiaryDescription> descriptions = diaryRepository.GetDiaryDescriptionsByDiaryId(diary.DiaryId);

// Fetch sections for selected project.
var projectSections = new List<Section>();
projectSections = diaryRepository.GetSectionsByProjectId(diary.ProjectId);

// Convert the Section model into a view model.
this.projectSections = new List<SectionViewModel>(
(from section in projectSections
select new SectionViewModel(section))
.ToList());

foreach (var projectSection in this.projectSections)
{
// We want to set ourself to Dirty if any child View Model becomes dirty.
projectSection.PropertyChanged += (sender, args) => this.IsDirty = true;
}

// Reconstruct our descriptions
this.DiaryDescriptions = new ObservableCollection<DiaryDescriptionDetailsViewModel>();
foreach (DiaryDescription description in descriptions)
{
SectionViewModel section =
this.projectSections.FirstOrDefault(s => s.Items.Any(i => i.BidItemId == description.BidItemId));
BidItem item = section.Items.FirstOrDefault(i => i.BidItemId == description.BidItemId);

var details = new DiaryDescriptionDetailsViewModel(description, section, item);
// Commenting this out resolves the NULL Reference Exception.
details.PropertyChanged += (sender, args) => this.IsDirty = true;

this.diaryDescriptions.Add(details);
}

this.IsDirty = false;
}

public ObservableCollection<DiaryDescriptionDetailsViewModel> DiaryDescriptions
{
get
{
return this.diaryDescriptions;
}

set
{
this.diaryDescriptions = value;
this.OnPropertyChanged();
}
}

public DiaryDescriptionDetailsViewModel SelectedDiaryDescription
{
get
{
return this.selectedDiaryDescription;
}

set
{
this.selectedDiaryDescription = value;

if (value != null)
{
// If the description contains a biditem DiaryId, then we go fetch the section and biditem
// associated with the diary description.
if (value.BidItemId > 0)
{
SectionViewModel sectionViewModel = this.ProjectSections.FirstOrDefault(
section => section.Items.FirstOrDefault(item => item.BidItemId == value.BidItemId) != null);

if (sectionViewModel != null)
{
BidItem bidItem = sectionViewModel.Items.FirstOrDefault(item => item.BidItemId == value.BidItemId);

this.selectedDiaryDescription.Section = sectionViewModel;
this.selectedDiaryDescription.BidItem = bidItem;
}
}

this.selectedDiaryDescription.IsDirty = false;
}

this.OnPropertyChanged();
this.IsDirty = false;
}
}

public List<SectionViewModel> ProjectSections
{
get
{
return this.projectSections;
}

set
{
this.projectSections = value;
this.OnPropertyChanged();
}
}

subview 模型:

public class DiaryDescriptionDetailsViewModel : BaseChangeNotify
{
private readonly DiaryDescription diaryDescription;

private SectionViewModel section;

private BidItem bidItem;

public DiaryDescriptionDetailsViewModel(DiaryDescription description)
{
this.diaryDescription = description;

// If we have a valid biditem identifier (greater than 0) than we need to go and
// fetch the item and it's associated funding section.
if (description.BidItemId > 0)
{
var repository = new DiaryRepository();
this.section = new SectionViewModel(repository.GetSectionByBidItemId(description.BidItemId));
this.bidItem = repository.GetBidItemById(description.BidItemId);
}

this.IsDirty = false;
}

public DiaryDescriptionDetailsViewModel(DiaryDescription description, SectionViewModel section, BidItem item)
{
this.diaryDescription = description;

if (description.BidItemId > 0)
{
this.section = section;
this.bidItem = item;
}

this.IsDirty = false;
}

public int Id
{
get
{
return this.diaryDescription.DiaryDescriptionId;
}
}

public int DiaryId
{
get
{
return this.diaryDescription.DiaryId;
}
}

public DiaryDescription Description
{
get
{
return this.diaryDescription;
}
}

public int BidItemId
{
get
{
return this.diaryDescription.BidItemId;
}
}

public BidItem BidItem
{
get
{
return this.bidItem;
}

set
{
this.bidItem = value;
this.diaryDescription.BidItemId = value.BidItemId;
this.OnPropertyChanged();
}
}

public SectionViewModel Section
{
get
{
return this.section;
}

set
{
this.section = value;
this.OnPropertyChanged();
}
}
}

因此在我的单元测试中,我使用了以下代码:

var diaryRepository = new DiaryRepository();
Diary diary = diaryRepository.GetDiaryById(DiaryId);
var diaryDescriptionViewModel = new DiaryDescriptionViewModel(diary, new UserViewModel());

// Act
diaryDescriptionViewModel.SelectedDiaryDescription =
diaryDescriptionViewModel.DiaryDescriptions.FirstOrDefault(
desc => desc.Id == DiaryDescriptionId);

这是堆栈跟踪:

Test Name:  DeleteDiaryDescriptionsById
Test FullName: UnitTests.ViewModels.DiaryDescriptionViewModelTests.DeleteDiaryDescriptionsById
Test Source: c:\Users\UnitTests\ViewModels\DiaryDescriptionViewModelTests.cs : line 103
Test Outcome: Failed
Test Duration: 0:00:02.678712

Result Message:
Test method Pen.UnitTests.ViewModels.DiaryDescriptionViewModelTests.DeleteDiaryDescriptionsById threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
Result StackTrace:
at Pen.ViewModels.BaseChangeNotify.OnPropertyChanged(String propertyName) in c:\Users\ViewModels\BaseChangeNotify.cs:line 70
at ViewModels.BaseChangeNotify.set_IsDirty(Boolean value) in c:\Users\ViewModels\BaseChangeNotify.cs:line 43
at ViewModels.BaseChangeNotify.OnPropertyChanged(String propertyName) in c:\Users\ViewModels\BaseChangeNotify.cs:line 57
at ViewModels.DiaryDescriptionDetailsViewModel.set_Section(SectionViewModel value) in c:\Users\ViewModels\DiaryDescriptionDetailsViewModel.cs:line 158
at ViewModels.DiaryDescriptionViewModel.set_SelectedDiaryDescription(DiaryDescriptionDetailsViewModel value) in c:\Users\ViewModels\DiaryDescriptionViewModel.cs:line 163
at UnitTests.ViewModels.DiaryDescriptionViewModelTests.DeleteDiaryDescriptionsById() in c:\Users\UnitTests\ViewModels\DiaryDescriptionViewModelTests.cs:line 112

它似乎在告诉我与 IsDirty 关联的对象为空,但事实并非如此。我通过调试器验证它存在并取消注释 DiaryDetailDescriptionViewModel.PropertyChanged 事件注册它工作正常。我做错了吗?

最佳答案

Application.Current 从单元测试运行时为空。

如果您想在单元测试中为调度程序安排内容,或者注入(inject)调度程序本身,则需要将其抽象到某个接口(interface)后面。

关于c# - INotifyPropertyChanged 奇怪的 NullReferenceException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23697237/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com