窗体之间的传值(先这么表达,不接受反驳)

mingcsharp 2019-04-29 05:33:48
https://codedefault.com/2014/csharp-winform-pass-value-between-forms-by-delegate-and-event
这个连接给出这种事件传值算是一种非常广泛的方式,
今天我想问题的,这种方式,如果那个窗体关闭了,前面那个窗体中的事件是不是没有解除,是不是泄露了
...全文
2637 38 打赏 收藏 转发到动态 举报
写回复
用AI写文章
38 条回复
切换为时间正序
请发表友善的回复…
发表回复
泡泡龙 2019-05-03
  • 打赏
  • 举报
回复
frmAddr 释放了,AddressForm_ButtonClicked也就没外人引用了,自然随着类的释放而释放
泡泡龙 2019-05-03
  • 打赏
  • 举报
回复
你得先搞清楚GC的工作原理:在执行GC时,会挂起全部线程,并将托管堆中所有的内存都打上垃圾的标记,之后遍历所有可到达的实例,这些实例如果引用了托管堆的内存,就将该内存的标记由垃圾变为被引用。 当遇到A和B相互引用的时候,如果没有其他实例引用A或者B,虽然A和B相互引用,但是A和B都是不可到达的,即没办法引用A或者B,则A和B都会被判定为垃圾而被回收。讲解了这么一大堆,目的就是要说,在C#中,你想要释放一块内存,你只要让该块内存没有任何实例引用他,就可以了。 以上是百度抄的 *************************************************** 在你的代码里面的AddressUpdated引用的是AddressForm_ButtonClicked内存,这个内存和frmAddr 没关系,所以GC发现frmAddr 不会被引用,就会释放它。不会发生内存泄漏。 一般事件中发生内存泄漏,都是因为使用了静态事件,静态事件的生存期很长,造成事件引用的所有内存块都可以被GC访问到,无法释放,内存块的所在类也就无法释放,导致内存泄漏。 你这个代码没有使用静态事件,也没有使用ShowDialog,所以不会泄露的。
love氟利昂 2019-05-01
  • 打赏
  • 举报
回复
图片不见了,重新上传一个,楼主该给分了。 内存探查结果(快照1均为原始状态,未进行任何操作;快照2及之后,点击了主窗体的按钮,打开并关闭了子窗体,并点击探查器上的强制GC按钮) 原始参照:(主窗体未订阅事件,点击按钮仅打开了子窗体) 主窗体订阅了事件,但关闭时并未取消该事件。
love氟利昂 2019-05-01
  • 打赏
  • 举报
回复
先说得到的结论:子窗体关闭,不取消订阅事件并不会造成内存泄漏。@mingcsharp (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按钮) 原始参照:(主窗体未订阅事件,点击按钮仅打开了子窗体) 主窗体订阅了事件,但关闭时并未取消该事件。
love氟利昂 2019-04-30
  • 打赏
  • 举报
回复
引用 13 楼 OrdinaryCoder 的回复:
[quote=引用 11 楼 jflyseven 的回复:]
子窗体的代码,不知为什么没显示出来。

~ChildForm()
{
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());
}
}

确实,我之前做项目的时候也有这个疑问,我用WPF不过原理应该差不多,就是在子窗体Close的时候,子窗体的析构函数并没有被调用,无论开关多少次都是,最后在主窗体Close的时候会调用之前创建的所有子窗体的析构[/quote]
我调试了一下.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);
}
love氟利昂 2019-04-30
  • 打赏
  • 举报
回复
引用 16 楼 胖叔叔写代码 的回复:


Dispose是Form class内部的事件,无法重载,他做的是一些基础的事。

看两张图就可以知道,在窗体的Dispose之前,一些窗体的基础属性是可见的,Dispose将回收这些部分的数据。
但是你对窗体附加的公有属性在你的窗体变量消失前都不会被回收。

所以这和传值关系不大,只是一个一般的生命周期问题。另外说一下窗体基于GDI和GDI+,所以你怕泄露可以像QQ一样用dxui或者用wpf,否则泄露根本用不着看内存和窗体内生命周期的…………

在子窗体Dispose之后,事件中的数组(委托数组)是没有被回收的,主窗体去调用方法触发事件时,主窗体的Label的值依旧会改变,那它属不属于泄漏呢?
正怒月神 2019-04-30
  • 打赏
  • 举报
回复
引用 7 楼 以专业开发人员为伍 的回复:
主窗体是客户,子窗体是服务,事件定义在子窗体,子窗体的定义并不依赖于主窗体,当子窗体被 Close 之后并不存在对子窗体对象的引用,因此子窗体可以被 GC 释放。 你所谓的“前面那个窗体中的事件”这个描述是含糊的、错误的。事件定义在子窗体而不是主窗体,只不过事件触发时会调用主窗体的委托方法。但是这正是“依赖倒置”的本质! 主窗体向子窗体注册委托方法,这正是“依赖倒置”,主窗体依赖子窗体而不是子窗体依赖主窗体,因此子窗体并不会产生泄露。 反之,假设另外一种设计,假设子窗体向主窗体注册委托,子窗体依赖主窗体而主窗体并不依赖子窗体,那么直接关闭子窗体则会产生泄露,子窗体需要把委托从主窗体的事件注销掉,然后才关闭。
是的,就是这个理。
  • 打赏
  • 举报
回复

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();
        }

    }
}
贴一下窗体源代码,窗体设计器我就不贴了,就一个按钮。项目也就一个窗体项目,就一个窗体。
  • 打赏
  • 举报
回复
Dispose是Form class内部的事件,无法重载,他做的是一些基础的事。 看两张图就可以知道,在窗体的Dispose之前,一些窗体的基础属性是可见的,Dispose将回收这些部分的数据。 但是你对窗体附加的公有属性在你的窗体变量消失前都不会被回收。 所以这和传值关系不大,只是一个一般的生命周期问题。另外说一下窗体基于GDI和GDI+,所以你怕泄露可以像QQ一样用dxui或者用wpf,否则泄露根本用不着看内存和窗体内生命周期的…………
eaqpi 2019-04-30
  • 打赏
  • 举报
回复
没那么复杂的

dim fm as new 弹出的Form with {.text =??? }
fm.show
if fm. DialogResult =ok then
textbox1.text = fm.textbox1.text
endif

不就完了吗?何必事件来事件去的。
OrdinaryCoder 2019-04-30
  • 打赏
  • 举报
回复
引用 11 楼 jflyseven 的回复:
子窗体的代码,不知为什么没显示出来。

~ChildForm()
{
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());
}
}

确实,我之前做项目的时候也有这个疑问,我用WPF不过原理应该差不多,就是在子窗体Close的时候,子窗体的析构函数并没有被调用,无论开关多少次都是,最后在主窗体Close的时候会调用之前创建的所有子窗体的析构
秋天之落叶 2019-04-30
  • 打赏
  • 举报
回复
神仙打架,反正我是看不明白,如果是我用,我会和#14楼的想法一样。
记得有高手说过,不要使用窗体间的引用,到现在我都不明白为什么,我只会用窗体间引用,哈哈
bloodish 2019-04-30
  • 打赏
  • 举报
回复
有些人喜欢回答问题时发散一点,有些人喜欢针对问题做解答。 大家都是好意的,热心的这就够了。 判断留给你自己做,相信你自己,但不要去针对别人,无论是工作中还是生活中。
引用 39 楼 mingcsharp 的回复:
[quote=引用 38 楼 bloodish 的回复:] [quote=引用 36 楼 胖叔叔写代码 的回复:] [quote=引用 32 楼 bloodish 的回复:] 可以明确告诉你,按你链接中的例子,子窗体不会被GC回收,存在泄漏的风险。 因为子窗体的事件被一个外部对象(父窗体)订阅着,而这个外部对象还没进GC的回收队列。 你可以自己去Dump验证
在主窗体的生命周期结束前,他订阅的事件就不会释放,他订阅事件的窗体也就不会释放。 gc本身就是一个依据生命周期回收的东西,哪怕你强制回收,也只能回收掉子窗体上那些已经过期的数据,还是无法回收他被订阅的事件。 所以这个东西就会产生你强制回收,窗体上的元素变为null,然后订阅事件激活,窗体上的元素变为你需要激活的数值。 解决方法也很简单,你关闭子窗体的时候取消订阅事件。[/quote] 你又展开了一点,答案已经明确给了,能理解多少就看楼主自己的了。[/quote] 你说的也许是对的,但是如果你仔细的看完所有的留言你会发现,他们的观点并不一至,我做为一个菜鸟,你叫我如何不纠结? 反正我看了上面所有的留言我觉得他们的观点并 不一致, 不一致, 不一致,[/quote]
mingcsharp 2019-04-30
  • 打赏
  • 举报
回复
引用 38 楼 bloodish 的回复:
[quote=引用 36 楼 胖叔叔写代码 的回复:] [quote=引用 32 楼 bloodish 的回复:] 可以明确告诉你,按你链接中的例子,子窗体不会被GC回收,存在泄漏的风险。 因为子窗体的事件被一个外部对象(父窗体)订阅着,而这个外部对象还没进GC的回收队列。 你可以自己去Dump验证
在主窗体的生命周期结束前,他订阅的事件就不会释放,他订阅事件的窗体也就不会释放。 gc本身就是一个依据生命周期回收的东西,哪怕你强制回收,也只能回收掉子窗体上那些已经过期的数据,还是无法回收他被订阅的事件。 所以这个东西就会产生你强制回收,窗体上的元素变为null,然后订阅事件激活,窗体上的元素变为你需要激活的数值。 解决方法也很简单,你关闭子窗体的时候取消订阅事件。[/quote] 你又展开了一点,答案已经明确给了,能理解多少就看楼主自己的了。[/quote] 你说的也许是对的,但是如果你仔细的看完所有的留言你会发现,他们的观点并不一至,我做为一个菜鸟,你叫我如何不纠结? 反正我看了上面所有的留言我觉得他们的观点并 不一致, 不一致, 不一致,
bloodish 2019-04-30
  • 打赏
  • 举报
回复
引用 36 楼 胖叔叔写代码 的回复:
[quote=引用 32 楼 bloodish 的回复:] 可以明确告诉你,按你链接中的例子,子窗体不会被GC回收,存在泄漏的风险。 因为子窗体的事件被一个外部对象(父窗体)订阅着,而这个外部对象还没进GC的回收队列。 你可以自己去Dump验证
在主窗体的生命周期结束前,他订阅的事件就不会释放,他订阅事件的窗体也就不会释放。 gc本身就是一个依据生命周期回收的东西,哪怕你强制回收,也只能回收掉子窗体上那些已经过期的数据,还是无法回收他被订阅的事件。 所以这个东西就会产生你强制回收,窗体上的元素变为null,然后订阅事件激活,窗体上的元素变为你需要激活的数值。 解决方法也很简单,你关闭子窗体的时候取消订阅事件。[/quote] 你又展开了一点,答案已经明确给了,能理解多少就看楼主自己的了。
love氟利昂 2019-04-30
  • 打赏
  • 举报
回复
我现在持有事件对于发布者和订阅者而言都是强引用,如果子窗体没有取消订阅事件就关闭的话,就会出现内存泄漏的问题。看不动源代码了,只能寄希望于大佬们了,坐等答案
  • 打赏
  • 举报
回复
引用 32 楼 bloodish 的回复:
可以明确告诉你,按你链接中的例子,子窗体不会被GC回收,存在泄漏的风险。 因为子窗体的事件被一个外部对象(父窗体)订阅着,而这个外部对象还没进GC的回收队列。 你可以自己去Dump验证
在主窗体的生命周期结束前,他订阅的事件就不会释放,他订阅事件的窗体也就不会释放。 gc本身就是一个依据生命周期回收的东西,哪怕你强制回收,也只能回收掉子窗体上那些已经过期的数据,还是无法回收他被订阅的事件。 所以这个东西就会产生你强制回收,窗体上的元素变为null,然后订阅事件激活,窗体上的元素变为你需要激活的数值。 解决方法也很简单,你关闭子窗体的时候取消订阅事件。
threenewbee 2019-04-30
  • 打赏
  • 举报
回复
引用 31 楼 wanghui0380 的回复:
[quote=引用 29 楼 mingcsharp 的回复:] wanghui0380 你是在扯蛋,我问释放,泄露的问题,谁叫你扯关闭了,我关闭点的窗体右上角的叉叉,你说指的什么? 再这样瞎扯蛋,请勿复言
果然如此,谁在扯蛋,一目了然。原来“我关闭点的窗体右上角的叉叉”不等于form.close(),高见,高人啊!跪一个[/quote] 不用跑,这个人就是来捣乱的,昨天已经领教过他的那一套了。已经按照版规处理。请不要因此而影响您分享技术的热情。
threenewbee 2019-04-30
  • 打赏
  • 举报
回复
引用 29 楼 mingcsharp 的回复:
wanghui0380 你是在扯蛋,我问释放,泄露的问题,谁叫你扯关闭了,我关闭点的窗体右上角的叉叉,你说指的什么? 再这样瞎扯蛋,请勿复言
你拿我开心,也就算了,你还和wanghui0380抬杠,真的就过分了。
bloodish 2019-04-30
  • 打赏
  • 举报
回复
可以明确告诉你,按你链接中的例子,子窗体不会被GC回收,存在泄漏的风险。 因为子窗体的事件被一个外部对象(父窗体)订阅着,而这个外部对象还没进GC的回收队列。 你可以自己去Dump验证
加载更多回复(18)

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧