C# (.NET) 具有依赖于拥有对象的生命周期的对象

C# (.NET) Objects with dependent life-cycle on owning objects

我来自 C++ 背景,希望 C# (.NET) 专家对下面的问题陈述有一些想法,我对解决方法持开放态度,但要求已冻结。

问题陈述:

  1. 拥有一个系统,一旦拥有的对象被删除就自动清理依赖对象(与下面解释的 GC 提供的有点不同。)

  2. 依赖对象可能有除其拥有对象以外的其他引用,但一旦拥有对象被删除,依赖对象就需要去

  3. 能够用存根对象(占位符)引用替换其他未完成的引用,因为实际对象不再存在

  4. 系统需要与对象无关,并且应该能够检测引用或将它们替换为从 System.Object (.net)

    [= 继承的任何对象的存根71=]

术语定义:

从属对象:始终需要所有者但也可能被其他对象引用的对象。然而,依赖对象的生命周期将 完全 由拥有对象拥有。如果删除拥有对象,则必须删除从属对象。

存根对象 这些是表示已删除引用的对象。

职能背景

为了能够支持我们的功能需求,我们需要一个系统来自动清除所有者被删除的依赖对象,然后它会用存根替换其他引用以指示它持有的对象已被删除或卸载,

用一个简单的例子来解释这个

  1. Time T1 - 假设我们创建了一个 Line 对象。由于创建一条线需要起点和终点,因此它创建了 2 个点(Pt1 和 Pt2)对象。 Point 对象被标记为 Dependent 对象,而 Line Object 是 Owner。所以在任何时候,如果我们删除 Line,它应该去删除 Pt1 和 Pt2。

  2. 时间 T2:我们创建了两个新点 Pt3 和 Pt4(它们现在是独立的对象)

  3. 时间 T3:我们创建一个引用(Pt2、Pt3 和 Pt4)的曲线对象。这里Pt2的生命周期由Line对象控制。

  4. 时间 T4:我们从图形中删除线对象,现在作为要求,此操作必须删除 Pt1 和 Pt2,因为它们是由线创建的,线对象已被删除。

  5. 时间 T5:由于曲线也引用了 Pt2,因此现在它的几何计算不完整,将引用存根对象。 Curve 对象将被标记为已损坏,以便在未来的某个时间点我们可以对其进行编辑以引用新的点。

有这个系统的关键问题是因为删除是由.NET系统控制的,我们无法控制它。想过如何在 C# 或 .NET 中实现这一点(在 C++ 中,我们可以完全控制内存管理,因此可以在删除指针并在内存中删除或替换它们之前确定指针的活动引用)。

我知道垃圾收集器有其自身的巨大优势,但这也是我们需要在基于 .NET 的 C# 模型中支持的关键要求。

任何想法、建议都将不胜感激。

通常您无法在 C# 中控制内存的释放。正如 Ameya 所建议的,你可以做的是有一个 "dirty" 标志。

Yes I thought about the Dirty field approach, but as i have said this needs to be managed by system level. If an object is marked as Dirty other objects

请注意,在 .NET 中有很多 类 可以做到这一点:许多 IDisposable 类(尤其是从 Stream 继承的!) Dispose()d,他们将 disposed 标记设置为 true,并在 properties/methods 中设置了 if (disposed) throw ObjectDisposedException()。在你的情况下你不应该这样做,你应该简单地 return;return (some default value);

public class ObjectWithReferences : IDisposable
{
    private List<ObjectWithReferences> childs;

    protected readonly ObjectWithReferences Parent;

    public bool IsDisposed { get; private set; }

    protected ObjectWithReferences(ObjectWithReferences parent)
    {
        Parent = parent;

        if (parent != null)
        {
            parent.AddChild(this);
        }
    }

    private void AddChild(ObjectWithReferences child)
    {
        if (IsDisposed)
        {
            child.Dispose();
            return;
        }

        if (childs == null)
        {
            childs = new List<ObjectWithReferences>();
        }

        childs.Add(child);
    }

    private void DisposeChilds()
    {
        if (childs == null)
        {
            return;
        }

        foreach (ObjectWithReferences child in childs)
        {
            if (!child.IsDisposed)
            {
                child.Dispose();
            }
        }

        childs = null;
    }

    public void Dispose()
    {
        if (!IsDisposed)
        {
            try
            {
                Dispose(true);
            }
            finally
            {
                try
                {
                    DisposeChilds();
                }
                finally
                {
                    IsDisposed = true;
                    GC.SuppressFinalize(this);
                }
            }
        }
    }

    ~ObjectWithReferences()
    {
        if (!IsDisposed)
        {
            try
            {
                Dispose(false);
            }
            finally
            {
                try
                {
                    DisposeChilds();
                }
                finally
                {
                    IsDisposed = true;
                }
            }
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        // Does nothing, not necessary to call!
    }
}

使用示例:

public class ExampleRoot : ObjectWithReferences
{
    public ExampleRoot() : base(null)
    {
    }

    public void Foo()
    {
        if (IsDisposed)
        {
            return;
        }

        // Do Foo things
    }

    public void CreateChild()
    {
        if (IsDisposed)
        {
            return;
        }

        // Auto-adds itself!
        var child = new ExampleChild(this);
    }
}

public class ExampleChild : ObjectWithReferences
{
    private byte[] BigBuffer = new byte[1000000];

    public ExampleChild(ExampleRoot parent) : base(parent)
    {
    }

    protected override void Dispose(bool disposing)
    {
        // The ExampleChild object has a very long possible lifetime,
        // because it will live even in the IsDisposed == true state,
        // so it is better to free even managed resources.
        BigBuffer = null;
    }
}

代码相当simple/clear...有两个示例类(一个Root和一个Child)。基本思想是一个 "special" 对象,ObjectWithReferences 保留其子对象的引用。它是 IDisposable,当调用 Dispose() 时(或完成时)它 Dispose() 它的所有子对象。您可以使用 类 从该对象继承。 methods/properties 中的每个人都应始终检查 IsDisposed 属性 以查看该对象是否已被处置。如果它已被处置,他们应该什么也不做,并且 return 默认值(0、nullstring.Empty、...)。请注意,如果此对象之一保留对大型托管对象(例如数组)的引用,这与建议的 .NET 指南相反,它应该 null 这些引用以使 GC 收集它们。

请注意,是构造函数将正在构建的对象添加到其父对象!

这里的正常做法是使用 WeakReference.

如果您需要存根行为是自动的,您可以这样做:

public class AutoStubbed<T> where T:class
{
  private WeakReference<T> _reference;
  private T _stub;
  private readonly Func<T> _stubFactory;
  public AutoStubbed(T value, T stub)
  {
    _reference = new WeakReference<T>(value);
    _stub = stub;
  }
  public AutoStubbed(T value, Func<T> factory)
  {
    _reference = new WeakReference<T>(value);
    _stubFactory = factory;
  }
  public T Target
  {
    get
    {
      T ret;
      if(_reference.TryGetTarget(out ret))
        return ret;
      if(_stub == null && _stubFactory != null)
        _stub = _stubFactory();
      return _stub;
    }
  }
}

然后将 T 键入对象和存根定义的接口,而不是对象的类型。