我有一些代码有很多重复。问题来自于我正在处理嵌套的IDisposable
类型。今天我有一些看起来像:
public void UpdateFromXml(Guid innerId, XDocument someXml)
{
using (var a = SomeFactory.GetA(_uri))
using (var b = a.GetB(_id))
using (var c = b.GetC(innerId))
{
var cWrapper = new SomeWrapper(c);
cWrapper.Update(someXml);
}
}
public bool GetSomeValueById(Guid innerId)
{
using (var a = SomeFactory.GetA(_uri))
using (var b = a.GetB(_id))
using (var c = b.GetC(innerId))
{
return c.GetSomeValue();
}
}
对于这些方法中的每一个,整个嵌套using
块都是相同的(显示了两个,但是大约有十个)。唯一不同的是当你到达using
块的内层时会发生什么。
我想的一种方法是做一些事情:
public void UpdateFromXml(Guid innerId, XDocument someXml)
{
ActOnC(innerId, c =>
{
var cWrapper = new SomeWrapper(c);
cWrapper.Update(someXml);
});
}
public bool GetSomeValueById(Guid innerId)
{
var result = null;
ActOnC(innerId, c => { result = c.GetSomeValue(); });
return result;
}
private void ActOnC(Guid innerId, Action<TheCType> action)
{
using (var a = SomeFactory.GetA(_uri))
using (var b = a.GetB(_id))
using (var c = b.GetC(innerId))
{
action(c);
}
}
这是有效的,它只是一种笨重的解析(作为人类)。 是否有人对如何减少围绕此类嵌套using
块的代码重复有任何其他建议?如果它们不是IDisposable
那么可能只会创建一个方法返回b.GetC(innerId)
的结果......但这不是这种情况。
答案 0 :(得分:1)
在Rx框架中有一个名为CompositeDisposable
http://msdn.microsoft.com/en-us/library/system.reactive.disposables.compositedisposable%28v=vs.103%29.aspx
不应该太难推出自己的(虽然非常精简版):
public class CompositeDisposable : IDisposable
{
private IDisposable[] _disposables;
public CompositeDisposable(params IDisposable[] disposables)
{
_disposables = disposables;
}
public void Dispose()
{
if(_disposables == null)
{
return;
}
foreach(var disposable in _disposables)
{
disposable.Dispose();
}
}
}
然后这看起来更清洁:
public void UpdateFromXml(Guid innerId, XDocument someXml)
{
var a = SomeFactory.GetA(_uri);
var b = a.GetB(_id);
var c = b.GetC(innerId);
using(new CompositeDisposable(a,b,c))
{
var cWrapper = new SomeWrapper(c);
cWrapper.Update(someXml);
}
}
答案 1 :(得分:1)
我喜欢BFree提供的答案作为开头,但我做了一些修改。
//Give it a better name; this isn't designed to be a general purpose class
public class MyCompositeDisposable : IDisposable
{
public MyCompositeDisposable (string uri, int id, int innerid)
{
A = SomeFactory.GetA(uri);
B = A.GetB(id);
C = B.GetC(innerId);
}
//You can make A & B private if appropriate;
//not sure if all three or just C should be exposed publicly.
//Class names are made up; you'll need to fix.
//They should also probably be given more meaningful names.
public ClassA A{get;private set;}
public ClassB B{get;private set;}
public ClassC C{get;private set;}
public void Dispose()
{
A.Dispose();
B.Dispose();
C.Dispose();
}
}
完成后,您可以执行以下操作:
public bool GetSomeValueById(Guid innerId)
{
using(MyCompositeDisposable d = new MyCompositeDisposable(_uri, _id, innerId))
{
return d.C.GetSomeValue();
}
}
请注意,MyCompositeDisposable可能需要在构造函数和Dispose方法中使用try / finally块,以便创建/销毁中的错误可以正确地确保没有任何内容被取消。
答案 2 :(得分:1)
您始终可以创建更大的上下文来管理应创建/处置的对象。然后编写一个方法来创建更大的上下文...
public class DisposeChain<T> : IDisposable where T : IDisposable
{
public T Item { get; private set; }
private IDisposable _innerChain;
public DisposeChain(T theItem)
{
this.Item = theItem;
_innerChain = null;
}
public DisposeChain(T theItem, IDisposable inner)
{
this.Item = theItem;
_innerChain = inner;
}
public DisposeChain<U> Next<U>(Func<T, U> getNext) where U : IDisposable
{
try
{
U nextItem = getNext(this.Item);
DisposeChain<U> result = new DisposeChain<U>(nextItem, this);
return result;
}
catch //an exception occurred - abort construction and dispose everything!
{
this.Dispose()
throw;
}
}
public void Dispose()
{
Item.Dispose();
if (_innerChain != null)
{
_innerChain.Dispose();
}
}
}
然后使用它:
public DisposeChain<DataContext> GetCDisposeChain()
{
var a = new DisposeChain<XmlWriter>(XmlWriter.Create((Stream)null));
var b = a.Next(aItem => new SqlConnection());
var c = b.Next(bItem => new DataContext(""));
return c;
}
public void Test()
{
using (var cDisposer = GetCDisposeChain())
{
var c = cDisposer.Item;
//do stuff with c;
}
}
答案 3 :(得分:0)
如果您的Dispoable
类型正确处置了所有可支配成员,则只需要一个使用声明。
例如,这个:
public bool GetSomeValueById(Guid innerId)
{
using (var a = SomeFactory.GetA(_uri))
using (var b = a.GetB(_id))
using (var c = b.GetC(innerId))
{
return c.GetSomeValue();
}
}
如果a类型为b和c的成员,并且在其处置方法中处理了b和c,可能会成为这个:
public bool GetSomeValueById(Guid innerId)
{
using (var a = SomeFactory.GetA(_uri))
{
return a.GetSomeValue();
}
}
class A : IDisposable
{
private a;
private b;
public A (B b, C c)
{
this.b = b; this.c = c;
}
public void Dispose()
{
Dispose(true);
}
protected void Dispose(bool disposing)
{
if (disposing)
{
b.Dispose();
c.Dispose();
}
}
}
然而,您必须修改工厂以将b和c注入a。