如何使用C#将对象生存期与其他对象生命周期联系起来

时间:2017-05-10 01:54:54

标签: c# .net garbage-collection

假设您有一个.net对象bufferObject,它在内部使用某种相当大的非托管内存缓冲区。它的实现方式是在处理(或完成)时释放其分配的内存。

然后,您创建一个不同的对象reusingBufferObject,并为其提供指向bufferObject内部缓冲区的指针。所以这两个对象本身并不了解彼此的存在。

如果bufferObject超出范围并被处置/最终确定,则内部缓冲区将被释放,reusingBufferObject将访问无效内存。

当然,通过更改两个对象的底层分类的设计并让reusingBufferObject引用bufferObject,可以避免整个问题。但是,我们假设这两个对象都是第三方类的实例,并且它们的设计无法更改。

一个示例案例是使用一些图像处理库。必须将内部图像对象转换为Bitmap对象以在屏幕上显示它,但出于性能原因,图像对象内部缓冲区必须由Bitmap对象重用,而不是仅仅被复制到它

是否可以将bufferObject的生命周期与reusingBufferObject的生命周期联系起来,以便只有在reusingBufferObject之后它才有资格进行垃圾回收,而无需手动跟踪生命周期?

修改

这里有一个简单的例子来说明问题:

namespace Example
{
    // Some third party class with no control over its design.
    // The Data property is the pointer to an unmanaged memory
    // buffer.
    public class ThirdPartyImage : IDisposable
    {
        public int Width { get; }
        public int Height { get; }
        public int Stride { get; }
        public PixelFormat Format { get; }
        public IntPtr Data { get; }

        public ThirdPartyImage(string filename);
        public Dispose();
    }

    public static class MyOwnExtensions
    {
        // Some method to reuse the internal memory buffer.
        public static Bitmap ToBitmap(this ThirdPartyImage image)
        {
            return new Bitmap(image.Width, image.Height, image.Stride, image.Format,
                image.Data);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Bitmap bitmap = Foo();

            // ThirdPartyImage is not referenced anymore and the internal buffer (Data)
            // will be freed when garbage collected.

            Bar(bitmap);
        }

        private static Bitmap Foo()
        {
            ThirdPartyImage image = ThirdPartyImage("foo.bmp");
            Bitmap bitmap = image.ToBitmap();

            return bitmap;
        }

        private static void Bar(Bitmap bitmap)
        {
            // Do some Bitmap related work here.
            // The buffer could already be freed though.
        }
    }
}

修改

我正在寻找的东西就像垃圾收集方法,它可以让你将对象的生命周期与另一个生命周期联系起来。

或者在收集reusingBufferObject / Bitmap或者有资格收集时收到某种通知,这样我就可以构建一些涉及帮助对象的自动机制,该辅助对象拥有对{{的强引用1}}和bufferObjectWeakReference

手动跟踪两个实际上不再需要的对象(除了内部使用的缓冲区),例如通过引入一个包装类,对我来说似乎过于复杂。

3 个答案:

答案 0 :(得分:1)

创建对象的人负责处理它们。如果您有objectAobjectA需要objectB,但这两个对象都是由第三个对象objectC创建的,那么objectC需要确保同时处理这两个对象。

这是最重要的要点:创作者负责处置

这是一些IoC容器的最大问题之一:它们创造了一些东西,但是当涉及到处理时,事情留给了其他人,这使事情变得非常棘手。

答案 1 :(得分:1)

好吧,似乎ConditionalWeakTable<TKey, TValue>正在做我想做的事。 通过稍微改变一下这个例子,它应该可以工作:

public static class MyOwnExtensions
{
    private static readonly ConditionalWeakTable<Bitmap, ThirdPartyImage>
        WeakBitmapImageTable = new ConditionalWeakTable<Bitmap, ThirdPartyImage>();

    public static Bitmap ToBitmap(this ThirdPartyImage image)
    {
        Bitmap bitmap = new Bitmap(image.Width, image.Height, image.Stride, image.Format,
            image.Data);
        WeakBitmapImageTable.Add(bitmap, image);

        return bitmap;
    }
}

答案 2 :(得分:0)

您问题的第一部分非常通用,无法按原样回答。我的回答只是尝试回答问题中的特定代码示例。

由于ThirdPartyImage实现了IDisposable,因此它的预期行为是它保留该缓冲区,直到您在其上调用Dispose()。如果您不想复制图像缓冲区,则必须在位图的整个生命周期内保留对ThirdPartyImage的引用,否则当缓冲区释放时,程序将在某个时刻崩溃并且位图无法呈现。

您可以通过多种方式执行此操作 - 使用ThirdPartyImage实例的全局映射,由位图键入(这很棘手且可能很快成为维护问题),或者您可以保留两个对象({{1}你传递给每个地方的一个类中的位图(你可以)。这样,当您准备摆脱位图时,您只需销毁持有者对象,该对象将在位图上调用ThirdPartyImage,然后在Dispose()上调用。

你可以试着想出一些方法来隐藏这种类似的设置,这些设置似乎可以自动化,但最后一个对象到达另一个对象并将它们联系在一起。只有你知道何时摆脱第一个物体是安全的,这样第二个物体也可以消失。