c# – NamedScope和垃圾回收
(这个问题最初是在Ninject Google Group中提出的,但我现在看到Stackoverflow似乎更活跃了.)
我正在使用NamedScopeExtension将相同的ViewModel注入到View和Presenter中.发布View后,内存分析显示Ninject缓存仍保留ViewModel.如何让Ninject发布ViewModel?表单关闭和处置时释放所有ViewModel,但我使用表单中的Factory创建和删除控件,并希望将ViewModel垃圾收集到(收集Presenter和View). 有关问题的说明,请参阅以下UnitTest,使用dotMemoryUnit: using System; using FluentAssertions; using JetBrains.dotMemoryUnit; using Microsoft.VisualStudio.TestTools.UnitTesting; using Ninject; using Ninject.Extensions.DependencyCreation; using Ninject.Extensions.NamedScope; namespace UnitTestProject { [TestClass] [DotMemoryUnit(FailIfRunWithoutSupport = false)] public class UnitTest1 { [TestMethod] public void TestMethod() { // Call in sub method so no local variables are left for the memory profiling SubMethod(); // Assert dotMemory.Check(m => { m.GetObjects(w => w.Type.Is<ViewModel>()).ObjectsCount.Should().Be(0); }); } private static void SubMethod() { // Arrange var kernel = new StandardKernel(); string namedScope = "namedScope"; kernel.Bind<View>().ToSelf() .DefinesNamedScope(namedScope); kernel.DefineDependency<View,Presenter>(); kernel.Bind<ViewModel>().ToSelf() .InNamedScope(namedScope); kernel.Bind<Presenter>().ToSelf() .WithCreatorAsConstructorArgument("view"); // Act var view = kernel.Get<View>(); kernel.Release(view); } } public class View { public View() { } public View(ViewModel vm) { ViewModel = vm; } public ViewModel ViewModel { get; set; } } public class ViewModel { } public class Presenter { public View View { get; set; } public ViewModel ViewModel { get; set; } public Presenter(View view,ViewModel viewModel) { View = view; ViewModel = viewModel; } } } dotMemory.Check断言失败,在分析快照时,ViewModel引用了Ninject缓存.我认为在View发布时应该发布命名范围. 问候, 解决方法
TL; DR
简短回答:将INotifyWhenDisposed添加到您的视图中.处理视图.这将导致ninject自动处理绑定InNamedScope的所有东西以及ninject将取消引用这些对象.这将导致(最终)垃圾收集(除非您在其他地方依赖于强引用). 为什么你的实现不起作用 当视图被释放/获得处置时,Ninject不会得到通知. 我相信默认情况下定时器设置为30秒. 那究竟是什么意思呢? >如果没有内存压力,GC可能需要很长时间,直到范围对象被垃圾收集(或者他可能不会这样做) 确定性地释放Scoped对象 现在,如果您需要在释放范围时立即处置/释放对象,则需要将 根据Ninject.Extensions.NamedScope的集成测试,这就足够了:见here 注意:唯一真正确定的是处理范围对象. 实现这个应该让单元测试通过. 注意:如果根对象绑定了InCallScope,则此解决方案不起作用(ninject 3.2.2 / NamedScope 3.2.0).我认为这是由于InCallScope的一个错误,但遗憾的是几年前我没有报告它(这个错误).不过,我可能也错了. 证明在根对象中实现INotifyWhenDisposed将处置子级 public class View : INotifyWhenDisposed { public View(ViewModel viewModel) { ViewModel = viewModel; } public event EventHandler Disposed; public ViewModel ViewModel { get; private set; } public bool IsDisposed { get; private set; } public void Dispose() { if (!this.IsDisposed) { this.IsDisposed = true; var handler = this.Disposed; if (handler != null) { handler(this,EventArgs.Empty); } } } } public class ViewModel : IDisposable { public bool IsDisposed { get; private set; } public void Dispose() { this.IsDisposed = true; } } public class IntegrationTest { private const string ScopeName = "ViewScope"; [Fact] public void Foo() { var kernel = new StandardKernel(); kernel.Bind<View>().ToSelf() .DefinesNamedScope(ScopeName); kernel.Bind<ViewModel>().ToSelf() .InNamedScope(ScopeName); var view = kernel.Get<View>(); view.ViewModel.IsDisposed.Should().BeFalse(); view.Dispose(); view.ViewModel.IsDisposed.Should().BeTrue(); } } 它甚至适用于DefineDependency和WithCreatorAsConstructorArgument 我没有dotMemory.Unit但是这会检查ninject是否保持对其缓存中对象的强引用: namespace UnitTestProject { using FluentAssertions; using Ninject; using Ninject.Extensions.DependencyCreation; using Ninject.Extensions.NamedScope; using Ninject.Infrastructure.Disposal; using System; using Xunit; public class UnitTest1 { [Fact] public void TestMethod() { // Arrange var kernel = new StandardKernel(); const string namedScope = "namedScope"; kernel.Bind<View>().ToSelf() .DefinesNamedScope(namedScope); kernel.DefineDependency<View,Presenter>(); kernel.Bind<ViewModel>().ToSelf().InNamedScope(namedScope); Presenter presenterInstance = null; kernel.Bind<Presenter>().ToSelf() .WithCreatorAsConstructorArgument("view") .OnActivation(x => presenterInstance = x); var view = kernel.Get<View>(); // named scope should result in presenter and view getting the same view model instance presenterInstance.Should().NotBeNull(); view.ViewModel.Should().BeSameAs(presenterInstance.ViewModel); // disposal of named scope root should clear all strong references which ninject maintains in this scope view.Dispose(); kernel.Release(view.ViewModel).Should().BeFalse(); kernel.Release(view).Should().BeFalse(); kernel.Release(presenterInstance).Should().BeFalse(); kernel.Release(presenterInstance.View).Should().BeFalse(); } } public class View : INotifyWhenDisposed { public View() { } public View(ViewModel viewModel) { ViewModel = viewModel; } public event EventHandler Disposed; public ViewModel ViewModel { get; private set; } public bool IsDisposed { get; private set; } public void Dispose() { if (!this.IsDisposed) { this.IsDisposed = true; var handler = this.Disposed; if (handler != null) { handler(this,EventArgs.Empty); } } } } public class ViewModel { } public class Presenter { public View View { get; set; } public ViewModel ViewModel { get; set; } public Presenter(View view,ViewModel viewModel) { View = view; ViewModel = viewModel; } } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |