将对对象的所有引用设为null,以便可以收集它

时间:2014-10-19 08:23:37

标签: c#

我正在尝试实现类似于Unity3D中GameObject的行为。

我在任何给定的应用程序中有许多对象引用任何给定数量的其他对象。每个对象都可以标记为删除,如果是;我需要知道如何取消对它的任何引用,以便它可以由GC收集并完全从范围中删除。我正在寻找一种方法来伪造这个或者实际上是这样做的。

出于所有意图和目的;如果一个对象引用另一个,并且被引用的对象被销毁,我需要引用看起来是null。如果我不能这样做,那么我唯一的另一个选择是每次使用它们时检查每个引用,并手动评估它是否被销毁,这是不切实际的。

3 个答案:

答案 0 :(得分:1)

我知道有两种方法与您所描述的很接近:

方法1:弱引用

仅使用WeakReference来引用对象,除了保留硬引用的一个'DataManager'之外。

然后您可以通过清除DataManager的硬引用来将实例“标记”为垃圾回收。

public class DataManager
{
    private readonly HashSet<object> data = new HashSet<object>();


    public void RegisterInstance(object instance)
    {
        data.Add(instance);
    }

    public void FreeInstance(object instance)
    {
        data.Remove(instance)
    }
}


public class Foo
{
    List<WeakReference<Bar>> bars = new List<WeakReference<Bar>>();

    private readonly DataManager dataManager;


    public Foo(DataManager dataManager)
    {
        this.dataManager = dataManager;
    }

    public void LogSomething()
    {
        bars.Add( Bar.Create(dataManager, 3, "Susan"));
        bars.Add( Bar.Create(dataManager, 4, "Bob"));
        bars.Add( Bar.Create(dataManager, 0, "Megan"));

        dataManager.FreeInstance(bars[1]);
        CG.Collect();

        for(int i = bars.Count - 1; i >= 0; i--;)
        {
            // hard reference to not loose the reference target between now and logging
            Bar bar;
            if(!bars[i].TryGetTarget(out bar)
            {
                bars.RemoveAt(i);
                continue;
            }

            Debug.Log($"{bar.value.Data2} has {bar.value.Data1} chocolate bars");
        }
    }
}


public class Bar
{
    public int Data1 { get; private set; }
    public string Data2 { get; private set; }

    private Bar(int data1, string data2)
    {
        Data1 = data1;
        Data2 = data2;
    }

    public static WeakReference<Bar> Create(DataManager dataManager, int data1, string data2)
    {
        var bar = new Bar(data1, data2);
        dataManager.RegisterInstance(bar);
        return new WeakReference(bar);
    }
}

缺点

  • 弱引用将继续指向标记为删除的目标,直到实际对其进行垃圾收集为止。
  • 在每次删除后运行垃圾收集器不是一个好主意

    • 很慢
    • 垃圾收集器甚至可能不会收集它。这意味着WeakReference可能会在删除后的某个时间突然丢失两行代码之间的引用。这要求您每次必须多次访问弱引用的目标时都保留硬引用。
  • 您必须在DataManager中注册每个创建的对象。

  • 忘记使用WeakReference来引用对象非常容易。
  • DataManager尖叫起来是静态的,这引起了红旗(至少对我来说)。

方法2:使用包装器

您可以将所有对象包装在通用类中,并且仅引用包装器。

public class Wraper<T>
{
    public T Value { get; private set; }


    public Wraper(T value)
    {
        Value = value;
    }

    public void Delete()
    {
        if(Value == null)
            throw new InvalidOperationException("Already deleted");
        Value = null;
    }

    public static implicit operator T(Wraper<T> wraper) => wraper.value;
    public static implicit operator Wraper<T>(T x) => new Wraper(x);
}


public class Foo
{
    List<Wraper<Bar>> bars = new List<Wraper<Bar>>();


    public void LogSomething()
    {
        bars.Add( new Bar(3, "Susan"));
        bars.Add( new Bar(4, "Bob"));
        bars.Add( new Bar(0, "Megan"));

        bars[1].Delete()
        for(int i = bars.Count - 1; i >= 0; i--;)
        {
            var bar = bars[i];
            if(bar.value == null)
            {
                bars.RemoveAt(i);
                continue;
            }

            Debug.Log($"{bar.value.Data2} has {bar.value.Data1} chocolate bars");
        }
    }
}


public class Bar
{
    public int Data1 { get; private set; }
    public string Data2 { get; private set; }

    public Bar(int data1, string data2)
    {
        Data1 = data1;
        Data2 = data2;
    }
}

您还可以覆盖equals的{​​{1}}方法以实现以下行为:

Wraper

我是用android应用程序编写的-示例代码可能有小错误

答案 1 :(得分:0)

您无法将this(又称自己&#39;)设置为null,以便丢失/销毁对您的引用。 this是只读的。

您可以做的事情是使用事件来告诉引用该实例的人将其设置为null。在将实例设置为null时,不要忘记取消订阅事件处理程序。

答案 2 :(得分:0)

我终于明白了实际发生了什么。 Unity具有C ++(因此,非托管)后端。您只与前端进行交互,但是对象具有内部标志和重载运算符,以给出引用为空的错觉。调用Destroy()方法时,该对象将释放其C ++组件并将其自身标记为已销毁。更多信息:http://answers.unity3d.com/questions/377510/does-destroy-remove-all-instances.html