如何使用 C# 将一个对象的生命周期与另一个对象的生命周期联系起来
How to tie an objects lifetime to another objects lifetime with C#
假设您有一个 .net 对象 bufferObject
,它在内部使用某种相当大的非托管内存缓冲区。它的实现方式是在处理(或完成)时释放其分配的内存。
然后您创建一个不同的对象 reusingBufferObject
并为其提供指向 bufferObject
的 内部缓冲区 的指针。所以这两个对象本身并不知道彼此的存在。
如果 bufferObject
超出范围并且是 disposed/finalized,内部缓冲区将被释放并且 reusingBufferObject
将访问无效内存。
当然可以通过更改两个对象的底层classed 的设计并让reusingBufferObject
引用bufferObject
来避免整个问题。但是我们假设这两个对象都是第三方 classes 实例化的,并且它们的设计无法更改。
一个例子是使用一些图像处理库。内部图像对象必须转换为 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
被收集或符合收集条件时发出某种通知,这样我就可以构建一些自动化机制,涉及一个对bufferObject
和一个 WeakReference
到 reusingBufferObject
.
手动跟踪两个实际上不再需要的对象(除了其内部使用的缓冲区),例如通过引入包装器 class,对我来说似乎过于复杂。
创建对象的人负责处置它们。如果你有 objectA
并且 objectA
需要 objectB
但两个对象都是由第三个对象 objectC
创建的,那么 objectC
需要确保释放它们.
这是最重要的一点要记住:创建者负责处置。
这是一些 IoC 容器的最大问题之一:它们创建东西,但在处理时,东西留给其他人,这让事情变得非常棘手。
你问题的第一部分非常笼统,无法按原样回答。我的回答只是试图回答你问题中的具体代码示例。
由于 ThirdPartyImage
实现了 IDisposable
,它的预期行为是它会保留该缓冲区,直到您对其调用 Dispose()
。如果不想复制图像缓冲区,则必须在位图的整个生命周期内保留对 ThirdPartyImage
的引用,否则当缓冲区被释放并且位图无法渲染。
您可以通过多种方式做到这一点 - 使用 ThirdPartyImage
个实例的全局映射,由位图作为键控(这很棘手,很快就会成为维护问题)或者您可以保留两个对象(ThirdPartyImage
和位图)在 class 中传递到每个地方(你可以)。这样,当您准备好摆脱位图时,您只需销毁 holder 对象,该对象将在位图上调用 Dispose()
,然后在 ThirdPartyImage
.
上调用
您可以尝试想出一些方法来将这种设置更深地隐藏在某些结构中,这些结构似乎可以自动执行此操作,但最后一个对象会进入另一个对象并将它们联系在一起。只有你知道什么时候可以安全地摆脱第一个对象,这样第二个对象也可以消失。
好吧,看来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;
}
}
假设您有一个 .net 对象 bufferObject
,它在内部使用某种相当大的非托管内存缓冲区。它的实现方式是在处理(或完成)时释放其分配的内存。
然后您创建一个不同的对象 reusingBufferObject
并为其提供指向 bufferObject
的 内部缓冲区 的指针。所以这两个对象本身并不知道彼此的存在。
如果 bufferObject
超出范围并且是 disposed/finalized,内部缓冲区将被释放并且 reusingBufferObject
将访问无效内存。
当然可以通过更改两个对象的底层classed 的设计并让reusingBufferObject
引用bufferObject
来避免整个问题。但是我们假设这两个对象都是第三方 classes 实例化的,并且它们的设计无法更改。
一个例子是使用一些图像处理库。内部图像对象必须转换为 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
被收集或符合收集条件时发出某种通知,这样我就可以构建一些自动化机制,涉及一个对bufferObject
和一个 WeakReference
到 reusingBufferObject
.
手动跟踪两个实际上不再需要的对象(除了其内部使用的缓冲区),例如通过引入包装器 class,对我来说似乎过于复杂。
创建对象的人负责处置它们。如果你有 objectA
并且 objectA
需要 objectB
但两个对象都是由第三个对象 objectC
创建的,那么 objectC
需要确保释放它们.
这是最重要的一点要记住:创建者负责处置。
这是一些 IoC 容器的最大问题之一:它们创建东西,但在处理时,东西留给其他人,这让事情变得非常棘手。
你问题的第一部分非常笼统,无法按原样回答。我的回答只是试图回答你问题中的具体代码示例。
由于 ThirdPartyImage
实现了 IDisposable
,它的预期行为是它会保留该缓冲区,直到您对其调用 Dispose()
。如果不想复制图像缓冲区,则必须在位图的整个生命周期内保留对 ThirdPartyImage
的引用,否则当缓冲区被释放并且位图无法渲染。
您可以通过多种方式做到这一点 - 使用 ThirdPartyImage
个实例的全局映射,由位图作为键控(这很棘手,很快就会成为维护问题)或者您可以保留两个对象(ThirdPartyImage
和位图)在 class 中传递到每个地方(你可以)。这样,当您准备好摆脱位图时,您只需销毁 holder 对象,该对象将在位图上调用 Dispose()
,然后在 ThirdPartyImage
.
您可以尝试想出一些方法来将这种设置更深地隐藏在某些结构中,这些结构似乎可以自动执行此操作,但最后一个对象会进入另一个对象并将它们联系在一起。只有你知道什么时候可以安全地摆脱第一个对象,这样第二个对象也可以消失。
好吧,看来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;
}
}