对象类型使用“ref”和/或“out”

时间:2008-10-28 20:50:28

标签: c# .net casting ref out

我坚持使用.Net 1.1应用程序(即我现在不能使用2.0中的泛型好东西),我试图优化代码的某些部分。由于它需要释放运行时可调用包装器,因此我最终创建了一个实用程序方法,该方法循环直到所有引用都被释放。该方法的签名是:

void ReleaseObject(object comObject)

释放所有comObject后,我调用GC.Collect和GC.WaitForPendingFinalizers(不要问 - 任何处理Office互操作的人都知道)。

并且...像往常一样,我遇到了一个问题 - 如果我在GC.Collect调用之前没有将相应的托管引用分配给null,则它不能正常清理。

所以,我的代码如下:

ReleaseObject(myComObject);
myComObject = null;
GC.Collect()
...

因为,有一堆xxx = null,我决定将它放在util方法中,但由于传递引用和传递引用参数之间存在差异,显然我不得不将方法更改为:

void ReleaseObject(out object comObject)
{
   //do release
   comObject = null;
}

并将来电者编辑为:

MyComClass myComObject = xxxx;
ReleaseObject(out myComObject);

此操作失败并显示一条消息:“无法从'输出MyComClass'转换为'输出对象'”

虽然我可以想到为什么它可能是一个问题(即从对象到MyComClass的反向转换不是隐含的,并且无法保证该方法将做什么),我想知道是否有解决方法,或者我需要留下我的数百个空值分配。

注意:我有一堆不同的COM对象类型,这就是为什么我需要一个“对象”参数,而不是一个类型安全的参数。

4 个答案:

答案 0 :(得分:3)

为什么调用方法比仅将变量设置为null更好?它们都是单线呼叫,而后者则更简单。

虽然你需要将它们设置为null,但听起来很奇怪。这些静态变量或实例变量的值是否需要在其包含对象之前发布?如果变量只是一个局部变量,无论如何都会超出范围,将其设置为null应该没有任何区别(在发布中)。

RCW没有实现IDisposable吗?如果是这样,调用Dispose(最好通过using语句)将是最好的选择。

(在评论中讨论后。)

这些是局部变量,稍后在方法中不会引用。这意味着垃圾收集器将意识到它们不需要被视为“根”引用 - 因此将它们设置为null应该没有任何区别。

然而,直接回答原始问题:不,你不能通过引用传递变量,除非方法参数的类型完全相同,所以你在这里运气不好。 (使用泛型它是可能的,但你已经说过你只限于.NET 1.1。)

答案 1 :(得分:3)

Sunny,ref和out是编译器的编组提示+合同。 Ref和out是COM日的遗留物 - 通过线路/进程之间发送对象的编组提示。

out合约

void foo( out MyClass x)
  1. foo()会在返回之前将x设置为某个内容。
  2. 输入foo()时,
  3. x没有值,如果在设置之前尝试使用x,则会出现编译错误。 (使用未分配的参数x)
  4. ref合约

    void foo( ref MyClass x)
    
    1. ref允许更改来电者参考。
    2. x必须是可分配的
      • 你不能把东西投射到中间变量foo(ref(object)something)
      • x不能是属性
    3. 最后两点的现实可能会阻止你做你想要做的事情,因为实际上当你明白什么是真正的参考时,它们就毫无意义。如果你想知道这一点,请问Jon Skeet(他写了一本书)。

      当编组ref时,它表示除了返回值之外,还要返回ref值。 当编组时,它表示在调用方法时不打扰发送out值,但是记住除了返回值之外还要返回out值。


      免责声明免责声明

      正如其他人所指出的,正在发生一些可疑的事情。你维护的蛮力代码似乎有一些微妙的错误,并且巧合编码。最好的解决方案可能是添加另一层间接。即包装器类的包装器,它确保确定性清理,您可以只编写一次杂乱的代码,而不是在整个代码库中编写它。


      那说..

      备选方案1

      除非你为每个类型的(com)对象提供重载,否则

      Ref不会做这个技巧。

      // need a remove method for each type. 
      void Remove( ref Com1 x ) { ...; x = null; }
      void Remove( ref Con2 x ) { ...; x = null; }
      void Remove( ref Com3 x ) { ...; x = null; }
      
      // a generics version using ref.
      void RemoveComRef<ComT>(ref ComT t) where ComT : class
      {
          System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
          t = null; 
      }
      
      Com1 c1 = new Com1();
      Com2 c2 = new Com2();
      Remove( ref c1 );
      RemoveComRef(ref c2); // the generics version again.
      

      备选方案2

      如果您不想这样做,请从Remove()方法返回null并转换回对象类型。

      class Remover
      {
          // .net 1.1 must cast if assigning
          public static object Remove(object x)
          {
              System.Runtime.InteropServices.Marshal.ReleaseComObject(x);
              return null;
          }
      
          // uses generics.
          public static ComT RemoveCom<ComT>(ComT t) where ComT : class
          {
              System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
              return null;
          }   
      }
      
      Com1 c1 = new Com1();
      Com2 c2 = new Com2();
      c1 = (Com1)Remover.Remove(c1); // no reliance on generics
      c2 = Remover.RemoveCom(c2); // relies on generics
      

      *我添加了通用版本进行比较。

      以上代码的效果是,在查看代码时,如果在没有赋值的情况下看到对Remove(x)的调用(使错误代码看起来错误),则会变得可疑。你甚至可以通过代码库来寻找调用Remove的调用。


      免责声明 - 上述所有内容都取决于您是否需要手动将引用设置为null,这通常是不必要的。

答案 2 :(得分:0)

您应该致电Marshal.ReleaseComObject,其中AFAIK在1.1中可用。

你可能的意思是“参考”:

static void ReleaseObject(ref object comObject)
{
   if(comObject != null)
   {
     //do release
     comObject = null;
   }
}

[编辑注释]但是,这只适用于无类型的对象,所以没有泛型就没用多少!哦,对于C#2.0 ......

重新“参考”;如果变量是真正的变量(意思是:方法变量),那么它们很快就会超出范围并被收集。 “ref”仅对释放字段有用。但说实话,将它们设置为空会更简单......

典型的COM模式是:

SomeType obj = new SomeType();
try {
  obj.SomeMethod(); // etc
} finally {
  Marshal.ReleaseComObject(obj);
}

答案 3 :(得分:0)

在我看来,你将无法在另一种方法中将这些对象设置为null(顺便说一下,你需要使用 ref 参数而不是 out 来制作它工作,无论如何你会遇到“无法转换...”错误同样的问题。) 我建议创建对象数组,然后迭代遍历该数组,调用ReleaseObject方法并将这些对象设置为null。类似的东西:

List garbagedObjects = new List();
garbagedObjects.Add(myComObject1);
garbagedObjects.Add(myComObject2);
...
foreach(object garbagedObject in garbagedObjects)
{
  ReleaseObject(garbagedObject);
  garbagedObject = null;
}
garbagedObjects = null;
GC.Collect();
...