https://codedefault.com/2014/csharp-winform-pass-value-between-forms-by-delegate-and-event
这个连接给出这种事件传值算是一种非常广泛的方式,
今天我想问题的,这种方式,如果那个窗体关闭了,前面那个窗体中的事件是不是没有解除,是不是泄露了
这个连接给出这种事件传值算是一种非常广泛的方式,
今天我想问题的,这种方式,如果那个窗体关闭了,前面那个窗体中的事件是不是没有解除,是不是泄露了
我写了一个Demo,才发现子窗体Close、Dispose只是释放不同的资源,哪怕是置空null和执行多次GC.Collect()后依旧如此,也并没有执行窗体析构函数并销毁对象。
没找到官方的文档说明,可能也只有进程被销毁时,才会销毁窗体的对象吧,关于这一点我也不是很清楚。 public MainForm()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
ChildForm child = new ChildForm();
//child.eventTest += eventTest;//订不订阅都一样
child.ShowDialog();
child.Close();
child.Dispose(); triggerGC(); child.TriggerEvent(); child = null; triggerGC();
} private void eventTest(string str)
{
this.Invoke((EventHandler)(delegate
{
this.label1.Text = str;
}));
} private void triggerGC()
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(); Thread.Sleep(500);
}
{
MessageBox.Show("Collect");
} public ChildForm()
{
InitializeComponent();
} public delegate void EventTest(string str);
public event EventTest eventTest; private void button1_Click(object sender, EventArgs e)
{
TriggerEvent();
} public void TriggerEvent()
{
if (eventTest != null)
{
eventTest(new Random().Next().ToString());
}
}
基础,如果窗体A发起事件,那么结果要在窗体B中执行,窗体B的执行结果与窗体A无关;如果窗体B结束以后,窗体A再发起事件,无窗体执行,函数不会重入,没有内存泄漏,只是窗体A的内存没有释放
fm.show
if fm. DialogResult =ok then
textbox1.text = fm.textbox1.text
endif不就完了吗?何必事件来事件去的。
但是你对窗体附加的公有属性在你的窗体变量消失前都不会被回收。所以这和传值关系不大,只是一个一般的生命周期问题。另外说一下窗体基于GDI和GDI+,所以你怕泄露可以像QQ一样用dxui或者用wpf,否则泄露根本用不着看内存和窗体内生命周期的…………
using System.Threading.Tasks;
using System.Windows.Forms;namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} public string test { get; set; } private void Button1_Click(object sender, EventArgs e)
{
Form1 child = new Form1();
child.test = "aaaaaa";
child.ShowDialog();
child.Dispose();
string test = child.test;
string testcap = child.Text;
} private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
this.Dispose();
} }
}
贴一下窗体源代码,窗体设计器我就不贴了,就一个按钮。项目也就一个窗体项目,就一个窗体。
我调试了一下.net framework源代码,发现窗体调用Close和Dispose时,会调用基类Componenet.cs中Dispose方法中,该方法中存在GC.SuppressFinalize(this)从而导致了不会再触发窗体析构函数的。 /// <devdoc>
/// <para>
/// Disposes of the <see cref='System.ComponentModel.Component'/>
/// .
/// </para>
/// </devdoc>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")]
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
题目问的就是子窗体自定义的事件没有解绑会不会导致泄漏呀,这是我没关系呀。
我也相信二位说的是对的,这么说的我给的那个连接的例子就不会有内存泄露了对吧基础,如果窗体A发起事件,那么结果要在窗体B中执行,窗体B的执行结果与窗体A无关;如果窗体B结束以后,窗体A再发起事件,无窗体执行,函数不会重入,没有内存泄漏,只是窗体A的内存没有释放
上面你说窗体A再发起事件,你这个窗体A本来就在,释放什么,你应该说的是窗体B没有释放吧?如果你是说的窗体B没有释放,那我想问B已经关了,为什么还不释放?
.Net的事件机制无法保证在对象Dispose之后,所有该对象作为发布者被订阅的事件完全取消订阅。这个例子中子窗体是事件的发布者,父窗体是事件的订阅者。
只要父窗体不再被持有任何引用,那么两者都可以进入GC的回收队列。
如果父窗体是你的主窗体,那么你有主动去取消订阅的义务。举个不同的例子,假如你的窗体中有很多的控件,控件的事件被窗体本身订阅,
当窗体不再被持有引用时,你就算没有取消这些事件订阅,窗体对象也可以正常被回收。
对于pinvoke也会看到“某些托管程序异常,无法访问XXXX地址”--这个在pinvoke回调时尤其明显
再这样瞎扯蛋,请勿复言
果然如此,谁在扯蛋,一目了然。原来“我关闭点的窗体右上角的叉叉”不等于form.close(),高见,高人啊!跪一个
可以明确告诉你,按你链接中的例子,子窗体不会被GC回收,存在泄漏的风险。
因为子窗体的事件被一个外部对象(父窗体)订阅着,而这个外部对象还没进GC的回收队列。
你可以自己去Dump验证
你拿我开心,也就算了,你还和wanghui0380抬杠,真的就过分了。
果然如此,谁在扯蛋,一目了然。原来“我关闭点的窗体右上角的叉叉”不等于form.close(),高见,高人啊!跪一个不用跑,这个人就是来捣乱的,昨天已经领教过他的那一套了。已经按照版规处理。请不要因此而影响您分享技术的热情。
在主窗体的生命周期结束前,他订阅的事件就不会释放,他订阅事件的窗体也就不会释放。
gc本身就是一个依据生命周期回收的东西,哪怕你强制回收,也只能回收掉子窗体上那些已经过期的数据,还是无法回收他被订阅的事件。所以这个东西就会产生你强制回收,窗体上的元素变为null,然后订阅事件激活,窗体上的元素变为你需要激活的数值。解决方法也很简单,你关闭子窗体的时候取消订阅事件。
在主窗体的生命周期结束前,他订阅的事件就不会释放,他订阅事件的窗体也就不会释放。
gc本身就是一个依据生命周期回收的东西,哪怕你强制回收,也只能回收掉子窗体上那些已经过期的数据,还是无法回收他被订阅的事件。所以这个东西就会产生你强制回收,窗体上的元素变为null,然后订阅事件激活,窗体上的元素变为你需要激活的数值。解决方法也很简单,你关闭子窗体的时候取消订阅事件。你又展开了一点,答案已经明确给了,能理解多少就看楼主自己的了。
在主窗体的生命周期结束前,他订阅的事件就不会释放,他订阅事件的窗体也就不会释放。
gc本身就是一个依据生命周期回收的东西,哪怕你强制回收,也只能回收掉子窗体上那些已经过期的数据,还是无法回收他被订阅的事件。所以这个东西就会产生你强制回收,窗体上的元素变为null,然后订阅事件激活,窗体上的元素变为你需要激活的数值。解决方法也很简单,你关闭子窗体的时候取消订阅事件。你又展开了一点,答案已经明确给了,能理解多少就看楼主自己的了。
你说的也许是对的,但是如果你仔细的看完所有的留言你会发现,他们的观点并不一至,我做为一个菜鸟,你叫我如何不纠结?
反正我看了上面所有的留言我觉得他们的观点并
不一致,
不一致,
不一致,
大家都是好意的,热心的这就够了。
判断留给你自己做,相信你自己,但不要去针对别人,无论是工作中还是生活中。在主窗体的生命周期结束前,他订阅的事件就不会释放,他订阅事件的窗体也就不会释放。
gc本身就是一个依据生命周期回收的东西,哪怕你强制回收,也只能回收掉子窗体上那些已经过期的数据,还是无法回收他被订阅的事件。所以这个东西就会产生你强制回收,窗体上的元素变为null,然后订阅事件激活,窗体上的元素变为你需要激活的数值。解决方法也很简单,你关闭子窗体的时候取消订阅事件。你又展开了一点,答案已经明确给了,能理解多少就看楼主自己的了。
你说的也许是对的,但是如果你仔细的看完所有的留言你会发现,他们的观点并不一至,我做为一个菜鸟,你叫我如何不纠结?
反正我看了上面所有的留言我觉得他们的观点并
不一致,
不一致,
不一致,
(1)首先说最最重要的一点是我找到MSDN关于取消事件的描述。画红线部分为只要发布者保持对事件的引用,GC就不会回收订阅的对象。这句话会不会意味着发布者不保持对事件的引用,GC就会回收订阅的对象呢,为了确保严肃性,我做了第二点的测试。(MSDN的链接:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events)(2)结论是:无论主窗体有无订阅事件,子窗体关闭后,内存和对象的个数都是相同的,也就是不存在内存泄漏。(第一次打开子窗体后都会出现内存上涨,GC后也并未释放的情况,猜测.net framework可能对Form的对象做了特殊的处理吧,如果有知道的大佬也请解答一下)
测试代码:
//主窗体 public MainForm()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
ChildForm child = new ChildForm();
child.EventTest += eventTest;//测试不存在委托时,该行注释
child.Show();
child.TriggerEvent();//测试不存在委托时,该行注释
child.Close();
} private void eventTest()
{ }
//子窗体 public ChildForm()
{
InitializeComponent();
} public delegate void DeEventTest();
public event DeEventTest EventTest; public void TriggerEvent()
{
if (EventTest != null)
{
EventTest();
}
}
内存探查结果(快照1均为原始状态,未进行任何操作;快照2及之后,点击了主窗体的按钮,打开并关闭了子窗体,并点击探查器上的强制GC按钮)
原始参照:(主窗体未订阅事件,点击按钮仅打开了子窗体)
主窗体订阅了事件,但关闭时并未取消该事件。
当遇到A和B相互引用的时候,如果没有其他实例引用A或者B,虽然A和B相互引用,但是A和B都是不可到达的,即没办法引用A或者B,则A和B都会被判定为垃圾而被回收。讲解了这么一大堆,目的就是要说,在C#中,你想要释放一块内存,你只要让该块内存没有任何实例引用他,就可以了。以上是百度抄的***************************************************在你的代码里面的AddressUpdated引用的是AddressForm_ButtonClicked内存,这个内存和frmAddr 没关系,所以GC发现frmAddr 不会被引用,就会释放它。不会发生内存泄漏。一般事件中发生内存泄漏,都是因为使用了静态事件,静态事件的生存期很长,造成事件引用的所有内存块都可以被GC访问到,无法释放,内存块的所在类也就无法释放,导致内存泄漏。你这个代码没有使用静态事件,也没有使用ShowDialog,所以不会泄露的。