0

我需要编写一个包装器,当代码超出当前范围时,对现有对象执行某些操作。

代码如下所示:

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都具有“更新”值。我还尝试ref在构造函数中传递数据,或者将数据作为属性放在ObjWrapper构造函数之后并将其设置,但都是一样的。知道为什么吗?我认为对于这种对象,c# 正在使用引用......

谢谢。

更新

4

5 回答 5

2

预期的行为是,当我在 Dispose 中放置断点时,我只看到数据的初始状态。

因为KnownType是一个而不是一个结构,所以你总是在ObjWrapper. 因此,您所做的任何更改也data将反映在包装器内部。

我认为对于这种对象,c# 正在使用引用......

是的,这就是为什么您会看到对象的当前状态。由于您正在存储对原始实例的引用,因此对实例的任何更改也将反映在该引用中。

如果要保存数据的副本,则需要自己制作副本。这可能意味着手动复制这些值。

于 2012-10-16T16:32:23.573 回答
1

很多问题仍然基于您未显示的代码,但现在足以做出有根据的猜测。

我会假设这KnownType是一个类。作为一个类,它是一个引用类型。这意味着任何变量(例如data)实际上并不包含KnownType对象包含的任何数据;KnownType该变量将仅包含存在“其他地方”的实际对象的引用(也称为内存中的位置) 。

当您将KnownType对象传递给构造函数时,您是按值传递它,但该值只是一个引用。这意味着您正在制作参考的副本。这两个引用都指向同一个实际对象。因此,如果它们都指向的实际对象发生了变化,那么两个变量都会“看到”变化。但是,如果您更改其中一个变量指向的对象,另一个变量将不知道该更改。因此,如果您没有看到您的更改反映在数据库中保存,这意味着data您不是在使用中改变现有对象,而是将一个全新的KnownType对象分配给该变量。(您还没有显示此代码,因此很难肯定地说。)

所以,第一个解决方案就是不要那样做。不要更改data指向的内容,只需更改data您传递给ObjWrapper.

如果您能够分配一个新对象并让该新对象成为被保存的对象真的很重要,那么KnownTypedata可以做这样的事情(如果您可以避免它,我不建议这样做,因为它在语义上相当奇怪,使其容易出错。)

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`
}
于 2012-10-16T21:00:08.047 回答
0

我找到了发生这种情况的原因:

  • 对象的初始状态(在进入构造函数之前)接近“新”(它的大多数成员,嗯,至少在监视窗口中可见的那些成员都具有“默认”值);
  • 在包装器的范围内,对象被传递给各种函数。其中一个基于某种逻辑,决定“重新创建”对象并用另一个对象的属性填充它的属性;
  • 当然,这意味着原始对象的引用在包装器的范围内发生了变化;
  • 在外面,在包装类'中,'引用'仍然'指向'旧对象。

鉴于新的事实,这是正常和预期的行为。我要感谢大家的帮助和建议!

于 2012-11-14T22:11:08.480 回答
0

您需要了解引用类型和值类型之间的区别。您不是在传递对象的副本,而是在传递对对象的引用。我猜您需要在 ObjWrapper 类的构造函数中实现/调用深层复制机制

于 2012-10-16T16:36:30.533 回答
0

你应该实现 Dispose() 方法来释放资源。如果您没有显式调用 Dispose() 方法,则无法确定它何时执行。建议不要写除资源释放以外的任何逻辑或代码。

于 2012-10-16T16:36:55.577 回答