9

非常に多くの重複があるコードがあります。問題は、ネストされた型を扱っているという事実から来てい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ブロック全体は、これらの各メソッドで同じです(2つが示されていますが、約10個あります)。using唯一異なるのは、ブロックの内側のレベルに到達したときに何が起こるかです。

私が考えていた1つの方法は、次のようなことをすることです。

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)ますが、ここではそうではありません。

4

4 に答える 4

1

Rxフレームワークには、 http://msdn.microsoft.com/en-us/library/system.reactive.disposables.compositedisposable%28v=vs.103%29.aspxというクラスがあります。CompositeDisposable

あなた自身を転がすのはそれほど難しいことではありません(非常に簡素化されたバージョンですが):

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);
    }
}
于 2012-04-23T17:52:26.820 に答える
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 / finalブロックを含める必要がある可能性が高いことに注意してください。これにより、作成/破棄のエラーにより、破棄されないものがなくなることが適切に保証されます。

于 2012-04-23T18:16:44.310 に答える
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;
        }
    }
于 2012-04-23T18:19:06.997 に答える
0

タイプがすべての使い捨てメンバーを正しく廃棄する場合、Dispoable必要なのは1つのusingステートメントのみです。

たとえば、これは次のとおりです。

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のメンバーがあり、disposeメソッドで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に注入するには、ファクトリを変更する必要があります。

于 2012-04-23T17:45:47.913 に答える