C# 我如何才能举行WeakReference<;行动>;在收集实例之前,是否将其复制到实例的方法? 类程序 { 静态void Main(字符串[]参数) { var inst=new SomeClass(); var weakRef=新的WeakReference(仪器剂量测量); GC.Collect(); WriteLine($“inst处于活动状态={inst!=null}:weakRef.Target处于活动状态={weakRef.TryGetTarget(out操作回调)}”); Console.ReadLine(); } } 公共类 { 公共void DoSomething(){} }

C# 我如何才能举行WeakReference<;行动>;在收集实例之前,是否将其复制到实例的方法? 类程序 { 静态void Main(字符串[]参数) { var inst=new SomeClass(); var weakRef=新的WeakReference(仪器剂量测量); GC.Collect(); WriteLine($“inst处于活动状态={inst!=null}:weakRef.Target处于活动状态={weakRef.TryGetTarget(out操作回调)}”); Console.ReadLine(); } } 公共类 { 公共void DoSomething(){} },c#,weak-references,C#,Weak References,输出显示inst不为空,但WeakReference指向的引用为空。我认为这是因为创建了一个指向实例方法的新操作,而不是存储对实例方法本身的引用 在实例尚未被垃圾收集的期间,如何保持对对象实例的方法的弱引用?从MethodGroup创建操作实例将创建一个未以任何方式附加到该方法所属类的实例的实例。这意味着没有任何东西可以让您的操作保持根目录,GC会很高兴地收集它 如果将对操作的引用存储为类的成员,则该引用将在该实例的生存期内生根,从而在该实例处于活动状态时阻止收集该引用 class Progra

输出显示
inst
不为空,但
WeakReference
指向的引用为空。我认为这是因为创建了一个指向实例方法的新操作,而不是存储对实例方法本身的引用


在实例尚未被垃圾收集的期间,如何保持对对象实例的方法的弱引用?

从MethodGroup创建
操作
实例将创建一个未以任何方式附加到该方法所属类的实例的实例。这意味着没有任何东西可以让您的
操作保持根目录,GC会很高兴地收集它

如果将对
操作的引用存储为类的成员,则该引用将在该实例的生存期内生根,从而在该实例处于活动状态时阻止收集该引用

class Program
{
    static void Main(string[] args)
    {
        var inst = new SomeClass();
        var weakRef = new WeakReference<Action>(inst.DoSomething);
        GC.Collect();
        Console.WriteLine($"inst is alive = {inst != null} : weakRef.Target is alive = {weakRef.TryGetTarget(out Action callback)}");
        Console.ReadLine();
    }
}

public class SomeClass
{
    public void DoSomething() { }
}
注意:我将原始的
DoSomething
设置为私有,并将其重命名为
DoSomething\u Internal
,使用只读属性替换旧的
DoSomething
,以便使类签名尽可能接近原始类。您不需要完全遵循此模式,任何对类中存储的
操作的引用(包括普通字段中的引用)都可以。尽管如果你真的想使用它,你还是必须以某种方式公开这个引用

您可以这样测试它:

public class SomeClass
{
    public SomeClass()
    {
        DoSomething = this.DoSomething_Internal ;
    }

    public Action DoSomething { get; } 

    private void DoSomething_Internal() { }
}
var inst=new SomeClass();
var weakRef=新的WeakReference(仪器剂量测量);
GC.Collect();
GC.WaitForPendingFinalizers();//您应该在强制执行GC后执行此操作,以防仍有GC工作在后台进行。
WriteLine($“inst是活动的={inst!=null}:weakRef.Target是活动的={weakRef.TryGetTarget(out Action callback1)}”);
//正如Hans在评论中指出的,接下来的两行丢弃了局部变量,
//不要在生产代码中执行此操作。请阅读他发布的链接以了解更多详细信息。
inst=null;//放弃类实例
callback1=null;//从TryGetTarget中丢弃临时操作实例,否则它将充当GC根,防止以后收集它。
GC.Collect();
GC.WaitForPendingFinalizers();
WriteLine($“inst是活动的={inst!=null}:weakRef.Target是活动的={weakRef.TryGetTarget(out Action callback2)}”);
将生成以下输出:

inst是活动的=真的:weakRef.Target是活动的=真的
inst处于活动状态=错误:weakRef.Target处于活动状态=错误


从MethodGroup创建
操作
实例会创建一个实例,该实例不会以任何方式附加到该方法所属类的实例。这意味着没有任何东西可以让您的
操作保持根目录,GC会很高兴地收集它

如果将对
操作的引用存储为类的成员,则该引用将在该实例的生存期内生根,从而在该实例处于活动状态时阻止收集该引用

class Program
{
    static void Main(string[] args)
    {
        var inst = new SomeClass();
        var weakRef = new WeakReference<Action>(inst.DoSomething);
        GC.Collect();
        Console.WriteLine($"inst is alive = {inst != null} : weakRef.Target is alive = {weakRef.TryGetTarget(out Action callback)}");
        Console.ReadLine();
    }
}

public class SomeClass
{
    public void DoSomething() { }
}
注意:我将原始的
DoSomething
设置为私有,并将其重命名为
DoSomething\u Internal
,使用只读属性替换旧的
DoSomething
,以便使类签名尽可能接近原始类。您不需要完全遵循此模式,任何对类中存储的
操作的引用(包括普通字段中的引用)都可以。尽管如果你真的想使用它,你还是必须以某种方式公开这个引用

您可以这样测试它:

public class SomeClass
{
    public SomeClass()
    {
        DoSomething = this.DoSomething_Internal ;
    }

    public Action DoSomething { get; } 

    private void DoSomething_Internal() { }
}
var inst=new SomeClass();
var weakRef=新的WeakReference(仪器剂量测量);
GC.Collect();
GC.WaitForPendingFinalizers();//您应该在强制执行GC后执行此操作,以防仍有GC工作在后台进行。
WriteLine($“inst是活动的={inst!=null}:weakRef.Target是活动的={weakRef.TryGetTarget(out Action callback1)}”);
//正如Hans在评论中指出的,接下来的两行丢弃了局部变量,
//不要在生产代码中执行此操作。请阅读他发布的链接以了解更多详细信息。
inst=null;//放弃类实例
callback1=null;//从TryGetTarget中丢弃临时操作实例,否则它将充当GC根,防止以后收集它。
GC.Collect();
GC.WaitForPendingFinalizers();
WriteLine($“inst是活动的={inst!=null}:weakRef.Target是活动的={weakRef.TryGetTarget(out Action callback2)}”);
将生成以下输出:

inst是活动的=真的:weakRef.Target是活动的=真的
inst处于活动状态=错误:weakRef.Target处于活动状态=错误


如果需要在
SomeClass
实例之前不会收集
Action
实例,则需要将
SomeClass
实例的引用添加到
Action
实例。它可以是指向
Action
实例的
SomeClass
实例字段,但如果不能更改
SomeClass
定义,则可以使用类动态附加字段

var inst = new SomeClass();
var weakRef = new WeakReference<Action>(inst.DoSomething);

GC.Collect();
GC.WaitForPendingFinalizers(); // You should do this after forcing a GC, just in case there is still GC work being done in the background.

Console.WriteLine($"inst is alive = {inst != null} : weakRef.Target is alive = {weakRef.TryGetTarget(out Action callback1)}");

// These next 2 lines discard local variables, as Hans points out in the comments,  
// DO NOT do this in production code.  Please read the link he posted for more details.
inst = null; // discard the class instance
callback1 = null; // discard the temporary Action instance from TryGetTarget, otherwise it will act as a GC Root, preventing it from being collected later.

GC.Collect();
GC.WaitForPendingFinalizers();

Console.WriteLine($"inst is alive = {inst != null} : weakRef.Target is alive = {weakRef.TryGetTarget(out Action callback2)}");

DelegateKeeper
类中,我使用
List
作为依赖对象类型,因此可以为每个类实例保留多个委托。我使用作为表的键,所以不需要单独传递实例。这不适用于匿名方法,因为它们可能在
目标中有编译器生成的闭包类