我需要在每个“foreach”迭代中释放COM对象吗?

14

这里是(可能的)问题:

我创建了一个COM对象,然后使用“foreach”循环遍历它返回的集合中的每个元素。我需要释放我迭代的集合中的每个单独元素吗?(请参见下面的代码。)如果需要,我无法想到一种有效的方法在“finally”语句中释放它,以防在操作该项时发生错误。

有什么建议吗?

private static void doStuff()
{
    ComObjectClass manager = null;

    try
    {
        manager = new ComObjectClass();
        foreach (ComObject item in manager.GetCollectionOfItems())
        {
            Log.Debug(item.Name);
            releaseComObject(item); // <-- Do I need this line?
                                    //     It isn't in a 'finally' block...
                                    //             ...Possible memory leak?
        }
    }
    catch (Exception) { }
    finally
    {
        releaseComObject(manager);
    }
}

private static void releaseComObject(object instance)
{
    if (instance != null)
    {
        try
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(instance);
        }
        catch
        {
            /* log potential memory leak */
            Log.Debug("Potential memory leak: Unable to release COM object.");
        }
        finally
        {
            instance = null;
        }
    }
}
2个回答

14

使用foreach语句遍历COM对象是不推荐的,因为它会在后台创建一个引用,你无法控制其释放。建议使用for循环,并确保不要在COM对象中使用两个点号

修改后代码如下:

try
{
    manager = new ComObjectClass();
    ComObject comObject = null;
    ComObject[] collectionOfComItems = manager.GetCollectionOfItems();
    try
    {
        for(int i = 0; i < collectionOfComItems.Count; i++)
        {
            comObject = collectionOfComItems[i];
            ReleaseComObject(comObject);
        }
    }            
    finally
    {
        ReleaseComObject(comObject);
    }
}
finally 
{
    ReleaseComObject(manager);
}

那会使整个循环变得更慢。 - nawfal
@nawfal 为什么这会使循环变慢? - Mark Avenius
1
取决于COM对象本身及其实现。当我使用for循环运行Word和Excel互操作自动化时,我发现它比运行foreach循环要慢得多。索引可能并不是真正好的。谁知道,如果它在内部进行线性搜索呢? - nawfal
1
我明白了,无论如何,foreach循环都会导致内存泄漏。因此,即使它在前期更快,后期也会减慢速度;-) - Mark Avenius
感谢您多年来的精心策划! - Sandy Gifford
显示剩余4条评论

0
另一种方法是创建自己的迭代器函数:
IEnumerable<ComObject> GetChildItems(this ComObjectClass manager) {
    ComObject comObject = null;

    ComObject[] collectionOfComItems = manager.GetCollectionOfItems();
    for (int i = 0; i < collectionOfComItems.Length; i++) {
        try {
            comObject = collectionOfComItems[i];

            yield return comObject;
        } finally {
            if (comObject != null)
                Marshal.ReleaseComObject(comObject);
        }
    }

    yield break;
}

private static void doStuff() {
    ComObjectClass manager = null;

    try {
        manager = new ComObjectClass();

        foreach (ComObject item in manager.GetChildItems()) {
            Log.Debug(item.Name);
        }
    } finally {
        releaseComObject(manager);
    }
}

我认为这样做可以使你的代码更易读,特别是当你需要多次迭代子项时。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接