是否有使用.NET的WeakEventManager的弱事件的示例实现?
我正在尝试通过遵循文档中的"Notes to Inheritors"来实现它,但它很模糊.例如,我无法弄清楚如何ProtectedAddListener从AddListener我的自定义管理器中的静态函数调用.
我想知道什么是最好的方式使用WeakEventManager(4.5很好)与提供DependencyPropertyChangedEventArgs的事件.这些不是从EventArgs派生的(出于性能原因),因此WeakEventManager无法正常运行.
任何指南,链接或提示将非常感谢!
我一直在使用WeakEventManager来避免内存泄漏,我开始过度使用它们.我为INotifyPropertyChanged创建了扩展方法,例如:
public static void AddWeakPropertyChanged(this INotifyPropertyChanged item, Action handler)
{
PropertyChangedEventManager.AddHandler(item, (s, e) => handler(e.PropertyName), string.Empty);
}
Run Code Online (Sandbox Code Playgroud)
现在我很快意识到,这不起作用.实际上,您无法真正使用匿名方法进行弱事件处理.(如果我理解正确,那么编译器为它创建一个'闭包类'(用于保存引用的值),它具有处理程序,但由于你的闭包类没有在任何地方被引用,因此GC将清除它,并且事件处理程序不会被叫)
问题1:这是正确的吗?我的意思是它是正确的,然后当使用匿名方法(或lambda)作为弱事件处理程序时,仅当GC没有同时运行时才调用处理程序(例如,它是不确定的)?
嗯,我非常感谢,所以我做了一些单元测试,以确保我做对了.在我进行以下单元测试之前,它似乎还可以:
class DidRun
{
public bool Value { get; set; }
}
class TestEventPublisher
{
public event EventHandler<EventArgs> MyEvent;
public void RaiseMyEvent()
{
if (MyEvent != null)
MyEvent(this, EventArgs.Empty);
}
}
class TestClosure
{
public DidRun didRun { get; set; }
public EventHandler<EventArgs> Handler { get; private set; }
public TestClosure()
{
this.Handler = new EventHandler<EventArgs>((s, e) => didRun.Value = true); …Run Code Online (Sandbox Code Playgroud) 我不喜欢不合标准的模式,但我正在对我的应用程序进行快速测试,并且遇到了这种奇怪的行为。
考虑一个公开事件的普通类,这里是非常常见的 PropertyChanged,但我认为可以是任何其他类。
订阅者选择通过 WeakEventManager 助手订阅事件。现在,“奇怪”的事情是实际的发件人引用:只要实例与订阅中使用的实例相同,一切都会顺利。但是,当您使用另一个对象时,不会发出通知。
同样,这不是一个好的模式,但我想知道这种限制是否有任何充分的理由,或者说这是一种错误。与其说是真正的需要,不如说是一种好奇心。
class Class1
{
static void Main(string[] args)
{
var c = new MyClass();
WeakEventManager<INotifyPropertyChanged, PropertyChangedEventArgs>.AddHandler(
c,
"PropertyChanged",
Handler
);
c.ActualSender = c;
c.Number = 123; //will raise
c.ActualSender = new Class1();
c.Number = 456; //won't raise
Console.ReadKey();
}
static void Handler(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Handled!");
}
}
class MyClass : INotifyPropertyChanged
{
public object ActualSender { get; set; }
private int _number;
public int Number
{
get { return this._number; …Run Code Online (Sandbox Code Playgroud) 我遇到了与 WeakEventManager 的使用相关的内存保留问题。使用 DotMemoryProfiler 对我的应用程序进行分析后,我发现每当我使用以下命令添加处理程序时WeakEventManager.AddHandler,都会导致向 ConditionalWeakTable<object, object> 添加一个实例,并且该条目将被保留,除非手动删除该处理程序(即使在析构函数中删除也不会\ xe2\x80\x99t 工作,你必须有一个函数来显式删除它)。
从下面的代码中,您可以看到手动调用 Detach 方法与通过析构函数调用它是有区别的。手动调用时,ConditionalWeakTable 会被正确收集并在 DotMemoryProfiler 快照中标记为“Dead”。然而,当从析构函数 ( ) 调用它时~Subscriber,ConditionalWeakTable 仍然存在。请注意,快照是在执行 Detach 之后拍摄的(因为序列打印在控制台中)。我还确保它在发布模式下运行,并且未选中“抑制 JIT 优化”选项。
这是显示幸存对象的 DotMemory 快照的比较:
\n\n您可能认为幸存的字节不多\xe2\x80\x99,但这在我们的实际应用程序中引起了问题,因为我们广泛使用 WeakEventManager 并通过析构函数取消订阅它,导致保留大量内存(超过12MB)。\n这是一个示例的屏幕截图,它可以在我们的实际应用程序中保留如此巨大的内存量。
\n\n为了提供更多上下文,这里是相关的代码片段:
\nclass Program\n{\n static void Main()\n {\n var isolator = new Action(() => { \n var publisher = new Publisher();\n var subscriber = new Subscriber();\n\n subscriber.Init(publisher);\n MemoryProfiler.GetSnapshot();\n\n //subscriber.Detach(publisher); // detach it manually won\'t cause issue\n });\n isolator();\n\n …Run Code Online (Sandbox Code Playgroud) 我正在框架中查找WeakEventManager的实现,该实现侦听DependencyProperties的更改.我发现唯一的弱属性更改事件侦听器PropertyChangedEventManager被设计用于实现INotifyPropertyChanged的类型,这让我感到有点困惑.
这是否意味着如果您收听DependencyProperty进行更改
DependencyPropertyDescriptor
.FromProperty(target, target.OwnerType)
.AddValueChanged(component, handler)
Run Code Online (Sandbox Code Playgroud)
我不必担心因事件登记而活着的实例泄露?
我正在努力适应WeakEventManager,但我偶然发现了以下内容:
和之间的唯一区别是,请忽略复制/粘贴错误 ;)ABstaticnameof
我找到了关于泛型和静态类型的答案,但我想知道那WeakEventManager在做什么A?不知何故,它可以null作为静态事件的来源。
我在寻找一个简单的答案,为什么static事件是好的,但static class因为TEventSource突然没有。
代码:
public class A
{
public static event EventHandler Event;
}
public static class B
{
public static event EventHandler Event;
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
WeakEventManager<A, EventArgs>.AddHandler(null, nameof(A.Event), (s, e) => { });
WeakEventManager<B, EventArgs>.AddHandler(null, nameof(B.Event), (s, e) => { });
}
}
Run Code Online (Sandbox Code Playgroud)
错误: …
我有以下例外
WindowsBase.dll 中发生类型为“System.ArgumentException”的未处理异常
附加信息:在“ConsoleApplication.ITest”类型上找不到“事件”事件。
在这个再现中:
using System.Windows; // add reference to WindowsBase
interface IBase
{
event EventHandler Event;
}
interface ITest : IBase { }
class Test : ITest
{
public event EventHandler Event;
}
class Program
{
static void Main(string[] args)
{
var test = new Test();
WeakEventManager<IBase, EventArgs>.AddHandler(test, "Event", (s, e) => { }); // works
WeakEventManager<ITest, EventArgs>.AddHandler(test, "Event", (s, e) => { }); // exception
}
}
Run Code Online (Sandbox Code Playgroud)
为什么找不到通过接口事件继承?此方法抛出异常。