我需要编写一个包装器,当代码超出当前范围时,在现有对象上执行某些操作。
代码如下所示:
public class ObjWrapper : IDisposable
{
private KnownType dt = null;
public ObjWrapper(KnownType data)
{
this.dt = data;
}
public void Dispose()
{
SaveKnownTypeInDB(this.dt);
}
}
电话会是这样的:
KnownType data = new KnownType();
// do something on `data`
using (ObjWrapper ow = new ObjWrapper(data))
{
// do something on `data`
}
我总是在数据库中获取来自对象原始状态的值。当我在Dispose()中放置一个断点时,我可以确认它有原始值。当我在堆栈上检查调用方法时,我在构造函数中传递的对象具有正确的值。我期望data
对象通过引用传递,并且在ObjWrapper
内调用的所有属性都具有'updated'值。我还试图在构造函数中使用ref
传递数据,或者将数据作为属性放到ObjWrapper
并在构造函数之后设置它,但它完全相同。任何想法为什么?我认为对于这种对象,c#正在使用引用...
感谢。
更新
答案 0 :(得分:2)
预期的行为是当我在Dispose中放置一个断点时,我只看到初始数据状态。
由于KnownType
是类而不是 struct ,因此您始终在ObjWrapper
内分配引用。因此,您对data
所做的任何更改都将反映在包装器内部。
我认为对于这种对象,c#正在使用引用...
这就是你看到对象当前状态的原因。由于您要存储对原始实例的引用,因此对该实例的任何更改也将反映在该引用中。
如果要保存数据的副本,则需要自行制作副本。这可能意味着手动复制值。
答案 1 :(得分:1)
问题的大部分仍然是基于你没有展示过的代码,但现在已经足够做出有根据的猜测了。
我会假设KnownType
是一个班级。作为一个类,它是一个参考类型。这意味着任何变量(例如data
)实际上不包含KnownType
对象包含的任何数据;变量只包含一个存在于“其他地方”的实际KnownType
对象的引用(也就是内存中的一个位置)。
当您将KnownType
对象传递给构造函数时,您将按值传递它,但该值只是一个引用。这意味着您正在制作参考的副本。这两个引用都指向同一个实际对象。因此,如果它们指向的实际对象都被更改,则两个变量都“看到”更改。但是,如果您更改哪个变量指向哪个对象,则另一个变量将不知道该更改。因此,如果您没有看到数据库中反映的更改,那么它的含义是,而不是改变使用中的现有data
对象,而是指定一个全新的KnownType
对象那个变量。 (你没有展示这个代码,所以很难肯定地说。)
所以,第一个解决方案就是不要那么做。请勿更改data
指向的内容,只需改变您传递到data
的{{1}}对象。
如果能够将新的ObjWrapper
分配给KnownType
并将新的data
设置为保存的那个,那么你可以做类似的事情(我不建议)如果你可以避免它,因为它在语义上相当奇怪,使它容易出错。)
public class ObjWrapper : IDisposable
{
private Func<KnownType> functor;
public ObjWrapper(Func<KnownType> functor)
{
this.functor = functor;
}
public void Dispose()
{
SaveKnownTypeInDB(functor());
}
}
并使用它:
KnownType data = new KnownType();
// do something on `data`
using (ObjWrapper ow = new ObjWrapper(() => data))
{
// do something on `data`
}
答案 2 :(得分:0)
您需要了解Reference和Value类型之间的区别。您没有传递对象的副本,而是传递对该对象的引用。我猜你需要在你的ObjWrapper类的构造函数中实现/调用深层复制机制
答案 3 :(得分:0)
你应该实现Dispose()方法来释放资源。如果您没有显式调用Dispose()方法,则无法确定何时执行它。建议不要编写除资源释放之外的任何逻辑或代码。
答案 4 :(得分:0)
我找到了发生这种情况的原因:
根据新的事实,这是正常和预期的行为。我要感谢大家的帮助和建议!