Рассмотрите следующий код C# с помощью COM-объекта.
MyComObject o = new MyComObject;
try
{
var baz = o.Foo.Bar.Baz;
try
{
// do something with baz
}
finally
{
Marshal.ReleaseComObject(baz);
}
}
finally
{
Marshal.ReleaseComObject(o);
}
Это выпустит COM-объекты o
и baz
, но не временные объекты, возвращенные o.Foo
и o.Foo.Bar
. Это может вызвать проблемы, когда те объекты содержат большой объем неуправляемой памяти или других ресурсов.
Очевидное, но ужасное решение было бы, для создания помех коду еще больше try-finally
и Marshal.ReleaseComObject
. См. C# + COM Interop, детерминированный выпуск
Как обходное решение, я создал класс помощника
class TemporaryComObjects: IDisposable
{
public C T(C comObject)
{
m_objects.Add(comObject);
return comObject;
}
public void Dispose()
{
foreach (object o in m_objects)
Marshal.ReleaseComObject(o);
}
}
Использование:
using (TemporaryComObjects t = new TemporaryComObjects())
{
MyComObject o = t.T(new MyComObject);
var baz = t.T(t.T(t.T(o.Foo).Bar).Baz);
// do something with baz
}
Мои вопросы: Есть ли потенциальные проблемы с этим кодом? У кого-либо есть более изящное решение?
Самым большим захватом было бы имя, T
; Добавление
могло бы быть более иллюзорным в использовании. Я бы также добавил , где T : class
к общему методу, но "беглый API" кажется полезным. Также я бы склонился к тому, чтобы немного сгладить код. Я также вижу некоторые способы использования Expression
API, чтобы пройтись по целому дереву и перехватить все промежуточные шаги, но это не было бы trivial - но представьте:
using(var com = new SomeWrapper()) {
var baz = com.Add(() => new MyComObject().Foo.Bar.Baz);
}
где это дерево выражений и мы получаем посредники автоматически.
(также можно Clear()
или null
список в Dispose()
)
Like so:
static class ComExample {
static void Main()
{
using (var wrapper = new ReleaseWrapper())
{
var baz = wrapper.Add(
() => new Foo().Bar.Baz);
Console.WriteLine(baz.Name);
}
}
}
class ReleaseWrapper : IDisposable
{
List<object> objects = new List<object>();
public T Add<T>(Expression<Func<T>> func)
{
return (T)Walk(func.Body);
}
object Walk(Expression expr)
{
object obj = WalkImpl(expr);
if (obj != null && Marshal.IsComObject(obj) && !objects.Contains(obj))
{
objects.Add(obj);
}
return obj;
}
object[] Walk(IEnumerable<Expression> args)
{
if (args == null) return null;
return args.Select(arg => Walk(arg)).ToArray();
}
object WalkImpl(Expression expr)
{
switch (expr.NodeType)
{
case ExpressionType.Constant:
return ((ConstantExpression)expr).Value;
case ExpressionType.New:
NewExpression ne = (NewExpression)expr;
return ne.Constructor.Invoke(Walk(ne.Arguments));
case ExpressionType.MemberAccess:
MemberExpression me = (MemberExpression)expr;
object target = Walk(me.Expression);
switch (me.Member.MemberType)
{
case MemberTypes.Field:
return ((FieldInfo)me.Member).GetValue(target);
case MemberTypes.Property:
return ((PropertyInfo)me.Member).GetValue(target, null);
default:
throw new NotSupportedException();
}
case ExpressionType.Call:
MethodCallExpression mce = (MethodCallExpression)expr;
return mce.Method.Invoke(Walk(mce.Object), Walk(mce.Arguments));
default:
throw new NotSupportedException();
}
}
public void Dispose()
{
foreach(object obj in objects) {
Marshal.ReleaseComObject(obj);
Debug.WriteLine("Released: " + obj);
}
objects.Clear();
}
}