- PR -

TreeViewの再帰的に呼び出しについて

投稿者投稿内容
かずい
常連さん
会議室デビュー日: 2008/09/30
投稿数: 28
お住まい・勤務地: 北海道札幌市
投稿日時: 2009-03-16 12:23
Kingさん

たぶん、「using」でくくっているので、処理完了時にリソース破棄(というか、ガベージさんよろしくって感じですかね。)
されることを期待していると思います。
そのため、明示的なクローズがないのだと思います。
(あんまり行儀がいいとはいえないかもしれないけど。)

あ、横道それました。すいません。



[ メッセージ編集済み 編集者: かずい 編集日時 2009-03-16 15:49 ]
King
ぬし
会議室デビュー日: 2008/06/20
投稿数: 284
投稿日時: 2009-03-16 12:37
かずいさん

そうですねー。
Dispose に Close を兼任させるっていうのが
GC まかせだと破棄されるのがはっきりしない
(不具合の原因になりえる)
ので私は絶対しません。

というか皆さんやってらっしゃるんでしょうか。
SL
大ベテラン
会議室デビュー日: 2008/05/02
投稿数: 183
投稿日時: 2009-03-16 14:29
お世話になります。

> サンプルを見直したほうがよいと思います。....考え方が誤っていると思われます
見直しました。なんとなく意味がわかってきました。
はっきりと理解できたわけではありませんが、SQLへの次の呼び出し部分の考え方が間違っていたようです。
子が親の印と言うかアドレスみたいな一意的なしるしが欠けていました。それを追加しパラメータの箇所を追加してやることでうまくいくようになりました。

それと、using() の使い方について議論があるようですが、ガバレージに任すのは、危険ですか?
今回は、サンプルよりコピペしてきたので気にしていませんでしたが、実際のところそうなんでしょうか?MSのヘルプには、「不要になったオブジェクトの格納用のメモリが自動的に解放されます。メモリは常に解放されるわけではありません。CLR がガベージ コレクションの実行を決定したときにメモリが解放されます。」とあり、解放は、タイミングによりけり的なことが書いてあります。

今回のコードは、とりあえず下記のように変更してみました。
コード:
   DbConnection db;

   using (db = factory.CreateConnection())
   {
          :
   }
   db.Close();

セラフ
ベテラン
会議室デビュー日: 2005/12/01
投稿数: 95
お住まい・勤務地: 東北の顔の形といえば
投稿日時: 2009-03-16 14:55
引用:

SLさんの書き込み (2009-03-16 14:29) より:
それと、using() の使い方について議論があるようですが、ガバレージに任すのは、危険ですか?
今回は、サンプルよりコピペしてきたので気にしていませんでしたが、実際のところそうなんでしょうか?MSのヘルプには、「不要になったオブジェクトの格納用のメモリが自動的に解放されます。メモリは常に解放されるわけではありません。CLR がガベージ コレクションの実行を決定したときにメモリが解放されます。」とあり、解放は、タイミングによりけり的なことが書いてあります。


争点はそこじゃないっす。DBのクローズをDisposeに任せちゃうのが気持ち悪いって話だと思います。usingがDisposeをやってくれてるのはわかりますよね?一般的な最近のDBではDisposeでCloseしてくれてると思うけど、明示されてない(ヘルプ等に記載がない)DBもあるから、他人任せにしないで自分でやったほうがConnectionのCloseが忘れられて参照だけ消えちゃうっていう事故も起きなにくいっと言うことではないでしょうか。

ちなみに、「using」の外でCloseしたら、開放されているかもしれないオブジェクトのメソッドを呼ぶことになりますが、それってヤバくないですか?例外が発生すると思うのですが・・・
お だ
会議室デビュー日: 2008/04/02
投稿数: 14
投稿日時: 2009-03-16 15:09
コネクションプールのサイズ以上に再帰呼出が発生したら、コネクションリークになりませんか?
という事を、 King さん は言いたいのではないでしょうか?

ミニマムコード
コード:
class Program
{
  private static int MaxPoolCount = 3;
  static void Main(string[] args)
  {
    ConnectionLeakTest(1);
  }
  static void ConnectionLeakTest(int poolCount)
  {
    using (SqlConnection conn = new SqlConnection(string.Format(@"〜(省略);Pooling=True;Max Pool Size={0}", MaxPoolCount)))
    {
      conn.Open();
      if (poolCount <= MaxPoolCount)
      {
        ConnectionLeakTest(poolCount + 1);
      }
    }
  }
}

King
ぬし
会議室デビュー日: 2008/06/20
投稿数: 284
投稿日時: 2009-03-16 15:39
セラフさん
お ださん
ありがとうございます。
その通りです。

GCに任せると Open しているコネクションが複数存在するかもしれない・・・。
この時、コネクションプールが最大数を超える可能性があるかも・・・。
って思うから明示的に Close したいんです。
例え Close してたとしても個人的には何回も Open したくないです。
(今回は関係ないですがトランザクション処理もしにくいし)

下記のようなイメージをしています。

コード:
static void Main()
{
    using (DbConnection baseCn = factory.CreateConnection())
    {
        baseCn.Open();

        try
        {
            CreateNode(baseCn);
        }
        finally
        {
            if (baseCn.State != ConnectionState.Closed)
            {
                baseCn.Close();
            }
        }
    }
}

private void CreateNode(ref DbConnection cn)
{
    while (...)
    {
        // DB処理
        ・・・

        // 再帰
        CreateNode(cn);
    }
}


他には上記のコードのConnection を Command に置き換えて
Parameters 以外の CommandText 等のプロパティはずっと同じものを使って
Parameters だけ DB 処理時に置き換えると言うのをやった事があります。

using の外で Close する発想は今までありませんでした。

あとガバレージじゃなくてガベージコレクション(GC)ですよね?
かずい
常連さん
会議室デビュー日: 2008/09/30
投稿数: 28
お住まい・勤務地: 北海道札幌市
投稿日時: 2009-03-16 15:52
ギャーおいらが、打ち間違ってる。
恥ずかしいから編集しちゃいました。

セラフさん
お ださん
 その通りですね。
 「お行儀がいい」という表現で伝わり難くなってしまった。
 フォローありがとうございます。
Azulean
大ベテラン
会議室デビュー日: 2008/01/04
投稿数: 123
お住まい・勤務地: 大阪府
投稿日時: 2009-03-16 23:00
私的にはIDisposable.DisposeでCloseせず、アンマネージリソースを残すクラスはIDisposableを正しく実装できているとは思えません。
http://msdn.microsoft.com/ja-jp/library/system.idisposable.dispose.aspx

現実に存在するかどうかは確かめていないので分かりません。


仮にIDisposable.DisposeメソッドでCloseメソッドを実行しないクラスがいるとします。
そのクラスをメンバー変数に持ち、IDisposable.Disposeメソッドで内部のメンバー変数のCloseメソッドを呼び出すラッパークラスを作って、usingパターンで使うというのは駄目でしょうか?
usingスコープはコンパイル時にtry-finallyに展開されるわけですから、usingスコープの中でtry-finallyを書くようだと、usingスコープの意味が薄くなっているので。

# finally句にif (baseCn != null) baseCn.Dispose(); と書けばusingがいらないことになります。

デメリットとしてはラッパークラス経由になるため、微妙にプロパティを経由する手間がかかることでしょうか。
# usingスコープ内の一時変数に代入することで逃げることはできます。

コード:
class Wrapper : IDisposable
{
    private DbConnection _connection;
    public Wrapper(DbConnection conn)
    {
        _connection = conn;
    }

    public DbConnection Body
    {
        get { return _connection; }
    }

    public void Dispose()
    {
        if (_connection != null)
        {
            if (_connection.State != ConnectionState.Closed) _connection.Close();
            _connection.Dispose();
            _connection = null;
        }
    }
}

スキルアップ/キャリアアップ(JOB@IT)