我有一个类Foo,它包含一个指针,从C dll非托管内存。它实现了IDisposable使用C dll释放非托管内存,并使用dll实现了一些其他功能。
public unsafe class Foo : IDisposable {
void* ptr;
internal Foo() {
/* initialize ptr from C dll */
};
public int getInfo() {
/* return info from ptr using dll */
}
public Dispose() {
/* free ptr using C dll */
}
}
我也有一个工厂返回Foo对象的范例:
public unsafe class FooFactory {
public Foo createFoo() {
return new Foo();
}
}
最后我有一个函数,它接受Foo作为参数,处理它并返回:
public Foo process(Foo f) {
/* do some stuff with f */
return f;
}
所有我想做的是防止用户创建Foo对象,将其存储在一个变量中,并将该变量传递给process
,并将结果保存到另一个变量,因为它可能在这样的例子中是不安全的:
Foo f2 = null;
using (var f1 = FooFactory.createFoo()) {
// inside one can work with f1 and f2
f2 = process(f1);
}
// but outside f1's ptr is already destroyed in C dll and f2 can not use it
f2.getInfo();
我希望他们只使用这些方法中的一种,因为它们是安全的:
using var f1 = FooFactory.createFoo();
f1 = process(f1);
// or
using var f1 = process(FooFactory.createFoo());
在c++中,我可以使用std::unique_ptr来做,因为它是不可复制的。在c#中有什么方法可以做到吗?
没有完全匹配的模式。
相反,如果我在离开using
块后试图使用f2
,我希望Foo
类型会引发ObjectDisposedException
。检查状态并抛出异常可能是getInfo()
(以及类似的)方法的首要职责之一。using
模式在c#开发人员中根深蒂固,编写这段代码的人不会对这种行为感到惊讶。
这也是你可能想要为c#类型实现终结器的罕见情况之一。
您不能禁止,但您可以通过添加一些代码使Foo
类更安全:
public unsafe class Foo : IDisposable
{
void* ptr;
internal Foo()
{
/* initialize ptr from C dll */
}
public int getInfo()
{
if (ptr is null) { // <==== test whether ptr is still valid!
return 0; // or throw an exception.
}
/* return info from ptr using dll */
}
public void Dispose()
{
/* free ptr using C dll */
ptr = null; // <==== set ptr = null when disposed!
}
}