我是否需要在每个'foreach'迭代中释放COM对象?

时间:2010-11-30 19:48:20

标签: c# com memory-management memory-leaks com-interop

这是(潜在的)问题:

我创建一个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 个答案:

答案 0 :(得分:13)

您不应该将foreach语句与COM对象一起使用,因为在您无法控制释放的场景后面进行引用。我会切换到for循环并确保never use two dots with COM objects.

这看起来的方式是:

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);
}

答案 1 :(得分: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);
    }
}

我认为这使您的代码更具可读性,尤其是当您需要多次遍历子项时。