委托执行的作用域疑惑

小宏 2017-06-26 09:40:47

//按钮事件
private void button3_Click(object sender, EventArgs e)
{
Console.WriteLine($"-------------Thread单击开始,进程数:{Thread.CurrentThread.ManagedThreadId}-------------");
Func<string> oldFunc = new Func<string>(
() =>
{
Thread.Sleep(8000);

return "我是返回值";
}
);
Func<string> newFunc = this.GetThred<string>(oldFunc);
newFunc();
Console.WriteLine($"-------------Thread单击结束,进程数:{Thread.CurrentThread.ManagedThreadId}-------------");
}
//调用方法
public Func<T> GetThred<T>(Func<T> func)
{
T t = default(T);
ThreadStart ts = () =>
{
t = func.Invoke();
Console.WriteLine($"aaaaaaaaaaaa{Thread.CurrentThread.ManagedThreadId}aaaaaaaaaaa");
};
Thread thread = new Thread(ts);
thread.Start();
return () =>
{
Console.WriteLine($"【前】方法里面############{Thread.CurrentThread.ManagedThreadId}###############");
thread.Join();
Console.WriteLine($"【后】方法里面############{Thread.CurrentThread.ManagedThreadId}###############");
return t;
};
}

疑问:
事件里面调用的方法返回委托,主线程执行委托方法的时候
委托方法里面的thread跟t的作用域是GetThred里面的,为啥在外面执行的时候还能还能捕获到这个信息了?
请大神赐教
...全文
182 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
正怒月神 2017-06-26
  • 打赏
  • 举报
回复
sp1234说的很明白了,其实就是引用还在。
xuggzu 2017-06-26
  • 打赏
  • 举报
回复
sp1234 说法很精辟。
对象存在与否(甚至存在的物理位置)和其可调用性才是写代码应该考虑的。
不明白楼主的问题想探究什么?我只看到楼主把线程和委托玩出花了,代码写的有够曲折的。。。
  • 打赏
  • 举报
回复
c# “作用域”这个说法其实是于你这个问题并没有直接关系。作用域是指所声明的变量的语义,也就是你能不能用一个变量来靠谱地表示一个对象(引用一个对象)。 但是作用域概念跟对象“存在”的本质,其实并没有直接关系。比如说离开了 t 变量的作用域,那么 t 所宿主的对象仍然还存在着,作为 GetThread 返回的委托的宿主对象,又被 newFunc 变量所引用的委托而引用着! 所以你纠结“作用域”,就好象水中捞月,而误以为是真实的月亮。变量作用域概念跟对象真实的存在性概念,其实只是间接相关。
小宏 2017-06-26
  • 打赏
  • 举报
回复
引用 5 楼 sp1234 的回复:
return () => { Console.WriteLine($"【前】方法里面############{Thread.CurrentThread.ManagedThreadId}###############"); thread.Join(); Console.WriteLine($"【后】方法里面############{Thread.CurrentThread.ManagedThreadId}###############"); return t; }; 这个叫做匿名委托,是 c# 差不多10年前出现的语法。你现在才学到。 匿名委托方法这里使用了 t 变量,实际上就是这样的实现方式

class X2348923<T>
{
    public T value;

    public T func()
    {
                Console.WriteLine($"【前】方法里面############{Thread.CurrentThread.ManagedThreadId}###############");
                thread.Join();
                Console.WriteLine($"【后】方法里面############{Thread.CurrentThread.ManagedThreadId}###############");
                return t;
    }
}        
public Func<T> GetThred<T>(Func<T> func)
{
    .............
    .............
    var xxx= new X2348923<T>();
    xxx.vlue = t;
    return xxx.func();
}
匿名委托中把变量 t 按照面向对象的机制而封装为临时创建对象实例的属性/字段,然后调用此对象的封装的方法。 此对象的作用域分什么 GetThread 没有关系,对象的函数委托被GetFhread 函数返回,那么 GetThread 函数结束之后,此对象当然还是不能释放的,因为还有引用。至于说什么时候释放,那要看调用者将来的纯粹动态的使用过程而定,什么时候没有引用这个对象了,它就被 GC 自动释放了。 这是那些满脑子只有 c/c++ 静态分配内存概念的人不容易理解的。因为 .net 是真正的面向对象的、自动管理(和释放)对象存储的,不需要开发人员低级地手动静态释放对象存储。
非常感谢
  • 打赏
  • 举报
回复
上面写错了变量名,改一下
class X2348923<T>
{
    public T value;
 
    public T func()
    {
                Console.WriteLine($"【前】方法里面############{Thread.CurrentThread.ManagedThreadId}###############");
                thread.Join();
                Console.WriteLine($"【后】方法里面############{Thread.CurrentThread.ManagedThreadId}###############");
                return this.value
    }
}   
如果有其他打字错误,细节自己理解吧。这里主要是了解这个匿名委托返回了一个编译器自动创建的对象类的实例(的方法委托),而这个对象跟什么线程无关!也跟GetThread 作用于无关。对象在于堆栈中,根本就是跨线程的。
  • 打赏
  • 举报
回复
return () => { Console.WriteLine($"【前】方法里面############{Thread.CurrentThread.ManagedThreadId}###############"); thread.Join(); Console.WriteLine($"【后】方法里面############{Thread.CurrentThread.ManagedThreadId}###############"); return t; }; 这个叫做匿名委托,是 c# 差不多10年前出现的语法。你现在才学到。 匿名委托方法这里使用了 t 变量,实际上就是这样的实现方式

class X2348923<T>
{
    public T value;

    public T func()
    {
                Console.WriteLine($"【前】方法里面############{Thread.CurrentThread.ManagedThreadId}###############");
                thread.Join();
                Console.WriteLine($"【后】方法里面############{Thread.CurrentThread.ManagedThreadId}###############");
                return t;
    }
}        
public Func<T> GetThred<T>(Func<T> func)
{
    .............
    .............
    var xxx= new X2348923<T>();
    xxx.vlue = t;
    return xxx.func();
}
匿名委托中把变量 t 按照面向对象的机制而封装为临时创建对象实例的属性/字段,然后调用此对象的封装的方法。 此对象的作用域分什么 GetThread 没有关系,对象的函数委托被GetFhread 函数返回,那么 GetThread 函数结束之后,此对象当然还是不能释放的,因为还有引用。至于说什么时候释放,那要看调用者将来的纯粹动态的使用过程而定,什么时候没有引用这个对象了,它就被 GC 自动释放了。 这是那些满脑子只有 c/c++ 静态分配内存概念的人不容易理解的。因为 .net 是真正的面向对象的、自动管理(和释放)对象存储的,不需要开发人员低级地手动静态释放对象存储。
exception92 2017-06-26
  • 打赏
  • 举报
回复
引用 3 楼 happyandsad 的回复:
[quote=引用 1 楼 duanzi_peng 的回复:] thread跟t的作用域是GetThred里面的 -》 thread 是由线程池(ThreadPool)创建,在合适的时机去取出来进行使用,所以没有“作用域” 这个说法。在后台代码的任何位置都可以查看当前线程ID
后台的任何位置查看的也只是当前线程信息吧?[/quote] 是的,也可以通过Thread.Name 来查看特定的,前提是设定Name。
小宏 2017-06-26
  • 打赏
  • 举报
回复
引用 1 楼 duanzi_peng 的回复:
thread跟t的作用域是GetThred里面的 -》 thread 是由线程池(ThreadPool)创建,在合适的时机去取出来进行使用,所以没有“作用域” 这个说法。在后台代码的任何位置都可以查看当前线程ID
后台的任何位置查看的也只是当前线程信息吧?
bloodish 2017-06-26
  • 打赏
  • 举报
回复
这是闭包(Closure)搞的鬼, Lambda表达式被编译器转换一个闭包类,通常是DisplayClassXXX之类的,用反编译工具(Refector,ILSpy)可以很清楚看到类型定义. 所有方法调用需要的变量状态都被这个闭包类保留(或者说驻留)了,所以就可以被访问到. 针对你的问题,举个浅显一点的例子. 在一个方法里实例化一个引用类型,把这个实例加到一个List,返回这个List,这个实例对象即使出了方法的作用域,也不会被回收,同理...
exception92 2017-06-26
  • 打赏
  • 举报
回复
thread跟t的作用域是GetThred里面的 -》 thread 是由线程池(ThreadPool)创建,在合适的时机去取出来进行使用,所以没有“作用域” 这个说法。在后台代码的任何位置都可以查看当前线程ID
闭包客 2017-06-26
  • 打赏
  • 举报
回复
引用 10 楼 hanjun0612 的回复:
sp1234说的很明白了,其实就是引用还在。
……是啊,就是引用还在啊。 楼主你的理解 thread 和 t 的作用域也是正确的啊,在外面执行的时候,使用的是其他的引用哇……只不过它们都指向同一个实例……

110,534

社区成员

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

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

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