可能重复:
在C#中取消订阅匿名方法
如何取消注册"匿名"事件处理程序
我最近发现我可以使用lambdas来创建简单的事件处理程序.我可以订阅像这样的点击事件:
button.Click += (s, e) => MessageBox.Show("Woho");
Run Code Online (Sandbox Code Playgroud)
但你怎么取消订阅呢?
是否可以从事件中取消订阅匿名方法?
如果我订阅这样的事件:
void MyMethod()
{
Console.WriteLine("I did it!");
}
MyEvent += MyMethod;
Run Code Online (Sandbox Code Playgroud)
我可以这样取消订阅:
MyEvent -= MyMethod;
Run Code Online (Sandbox Code Playgroud)
但是如果我使用匿名方法订阅:
MyEvent += delegate(){Console.WriteLine("I did it!");};
Run Code Online (Sandbox Code Playgroud)
是否有可能取消订阅这种匿名方法?如果是这样,怎么样?
说如果我听一个事件:
Subject.NewEvent += delegate(object sender, NewEventArgs e)
{
//some code
});
Run Code Online (Sandbox Code Playgroud)
现在我如何取消注册此活动?或者只是让内存泄漏?
我有以下代码让GUI响应集合中的更改.
myObservableCollection.CollectionChanged += ((sender, e) => UpdateMyUI());
Run Code Online (Sandbox Code Playgroud)
首先,这是一个很好的方法吗?
第二:取消订阅此活动的代码是什么?它是相同的但是 - =(然后又是完整的匿名方法)?
如果对象A侦听来自对象B的事件,则对象B将使对象A保持活动状态.是否存在可以阻止这种情况的弱事件的标准实现?我知道WPF有一些机制,但我正在寻找与WPF无关的东西.我猜测解决方案应该在某处使用弱引用.
假设我们有以下方法:
private MyObject foo = new MyObject();
// and later in the class
public void PotentialMemoryLeaker(){
int firedCount = 0;
foo.AnEvent += (o,e) => { firedCount++;Console.Write(firedCount);};
foo.MethodThatFiresAnEvent();
}
Run Code Online (Sandbox Code Playgroud)
如果实例化具有此方法的类并且PotentialMemoryLeaker多次调用该方法,那么我们是否会泄漏内存?
在我们完成调用之后有没有办法取消挂起lambda事件处理程序MethodThatFiresAnEvent?
UPDATE
我将这里的各种答案结合到一个新问题的"确定"答案中.
原始问题
在我的代码中,我有一个事件发布者,它在应用程序的整个生命周期中都存在(这里简化为基本要素):
public class Publisher
{
//ValueEventArgs<T> inherits from EventArgs
public event EventHandler<ValueEventArgs<bool>> EnabledChanged;
}
Run Code Online (Sandbox Code Playgroud)
因为这个发布者可以在所有地方使用,所以我对自己创建这个小帮助类非常满意,以避免在所有订阅者中重写处理代码:
public static class Linker
{
public static void Link(Publisher publisher, Control subscriber)
{
publisher.EnabledChanged += (s, e) => subscriber.Enabled = e.Value;
}
//(Non-lambda version, if you're not comfortable with lambdas)
public static void Link(Publisher publisher, Control subscriber)
{
publisher.EnabledChanged +=
delegate(object sender, ValueEventArgs<bool> e)
{
subscriber.Enabled = e.Value;
};
}
}
Run Code Online (Sandbox Code Playgroud)
它工作正常,直到我开始在较小的机器上使用它,当我偶尔开始:
System.ComponentModel.Win32Exception
Not enough storage is available to process …Run Code Online (Sandbox Code Playgroud) 除了多次使用它会违反DRY原则这个事实之外,你能否看到这种单线的缺点?这似乎很简单,但我没有看到其他人提出它的事实让我想知道它是否有缺点.
这段代码创建一个方法的WeakReference,然后注册一个调用引用目标的事件处理程序.
SomeEvent += (sender, e) => ((Action)(new WeakReference((Action)ProcessEvent)).Target)();
Run Code Online (Sandbox Code Playgroud)
谢谢,
本
我试图理解为什么在已从UI中删除的命令源上调用CanExecute.这是一个简化的程序来演示:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350" Width="525">
<StackPanel>
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding Txt}"
Command="{Binding Act}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="Remove first item" Click="Button_Click" />
</StackPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)
代码隐藏:
public partial class MainWindow : Window
{
public class Foo
{
static int _seq = 0;
int _txt = _seq++;
RelayCommand _act;
public bool Removed = false;
public string Txt { get { return _txt.ToString(); } }
public ICommand Act
{
get
{
if (_act == null) …Run Code Online (Sandbox Code Playgroud) 我正在开发一个自定义控件,它在内部订阅 Touch.FrameReported - 一个静态事件。这有可能导致内存泄漏(在某些情况下确实如此)。
这是我目前的解决方案。在加载/卸载事件中订阅/取消订阅。但是,我发现 Unloaded 事件并不总是被调用。这可能会导致内存泄漏。
// Imagine this is a CustomControl, to be consumed by users
// with no regard for calling Dispose
public class CustomGrid : Grid
{
public CustomGrid()
{
Loaded += (s, a) =>
{
Touch.FrameReported -= OnTouchFrameReported;
Touch.FrameReported += OnTouchFrameReported;
};
Unloaded += (s, a) =>
{
// The intention is to unsubscribe on unload, which should pre-date
// user intended 'disposal' of the control
Touch.FrameReported -= OnTouchFrameReported;
};
}
Run Code Online (Sandbox Code Playgroud)
有没有已知的模式来解决这个问题?取消订阅自定义控件“拆卸”中的事件?我已经尝试过:
我经常遇到想要订阅事件的情况,但我想使用lambda来执行此操作:
public class Observable
{
public event EventHandler SomethingHappened;
public void DoSomething()
{
// Do Something...
OnSomethingHappened();
}
}
// Somewhere else, I hook the event
observable.SomethingHappened += (sender, args) => Console.WriteLine("Something Happened");
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是我不知道如何取消事件.由于lambda在引擎盖下创建了一个匿名委托,所以我没有什么可以调用-=的.
现在,我可以创建一个方法:
private void SomethingHappened(object sender, EventArgs args)
{
Console.WriteLine("Something Happened");
}
Run Code Online (Sandbox Code Playgroud)
然后我可以挂钩/取消所有我想要的东西:
observable.SomethingHappened += SomethingHappened;
observable.SomethingHappened -= SomethingHappened;
Run Code Online (Sandbox Code Playgroud)
但我真的非常非常喜欢使用我的lambda.在一个更复杂的例子中,lambdas在这里非常方便.
我很确定我运气不好......但我想知道是否有人想出办法做到这一点?
我看到一些像这样的代码:
textBox.TextChanged += (s, e) => this.Foo();
Run Code Online (Sandbox Code Playgroud)
但我不知道"s"和"e"是什么?我应该在C#中学习这行代码的主题是什么?