プロが教えるわが家の防犯対策術!

visual C# 2010 Express を使用しています。
MySqlConnectionのデータベースのコネクション部分だけを次のようなメソッドとして、
そのオブジェクトだけを得たいと思いまして、次のような感じにしたのですが、

class Program
{
static void Main(string[] args)
{
MySqlConnection conn = GetConnection();
using (MySqlCommand cmd = new MySqlCommand("SELECT * FROM tbl where id = '1'", conn))
{
conn.Open();
MySqlDataReader reader = null;
reader = cmd.ExecuteReader();
reader.Read();
int i = reader.GetInt32(reader.GetOrdinal("id"));
Console.WriteLine(i.ToString());
Console.ReadLine();
conn.Close();
}
}

static MySqlConnection GetConnection()
{
string connStr = "server=localhost;Database=test;Uid=root;Pwd=";
using (MySqlConnection conn = new MySql.Data.MySqlClient.MySqlConnection(connStr))
{
return conn;
}
}
}


一応これで表示できたのですが、
これで生成したMySqlConnectionはちゃんと開放(消滅)されているのでしょうか?
このようなやり方でデータを取得しているのを見たことがないで
大丈夫なのかな?と思いました。。

A 回答 (2件)

個人的には,「えっ,これで動くの?」という感じですが……。



using文は,そのブロックから出たタイミングでオブジェクトをDisposeします。
このため,GetConnectionメソッドの戻り値は「破棄されたオブジェクト」になり,
本来はOpenメソッドがObjectDisposedExceptionを発生させなければなりません。

書き換えるなら,こうでしょうか。
class Program
{
static void Main(string[] args)
{
using (MySqlConnection conn = GetConnection())
using (MySqlCommand cmd = new MySqlCommand("SELECT * FROM tbl where id = '1'", conn))
{
conn.Open();
using (reader = cmd.ExecuteReader()) // IDataReaderはIDisposableなのでDisposeを呼ぶ必要がある
{
reader.Read();
int i = reader.GetInt32(reader.GetOrdinal("id"));
Console.WriteLine(i.ToString());
} // ここでreaderが解放される
conn.Close(); // 書いた方が行儀が良いが,省略可能
} // ここでcmdとconnが解放される
Console.ReadLine();
}

static MySqlConnection GetConnection()
{
const string connStr = "server=localhost;Database=test;Uid=root;Pwd="; // constを付けておくと,変更不可能になる
return new MySql.Data.MySqlClient.MySqlConnection(connStr);
}
}
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
これは今も試しましたが確かに取得できるのです。。
自分的にはもちろんなぜだかは分からないのですが、
最初はシンプルなIDisposableを継承した

class class1 : IDisposable
{
public int i = 2;
public void Dispose()
{
Console.WriteLine("Dispose");
Console.ReadLine();
}
}

でGetConnection()のような使い方を試してみたのですが、これも値が変化しました。
もしお手数でなければ、Yune-Kichiさんの環境でも試してみて頂けないでしょうか?

そして、提示して頂いたコードですが
なるほど、たしかにこのやり方ならMySqlConnectionを
GetConnectionに収めることができますね。
そもそもなんでこんなことを考えたかというと、
これも先ほどのPHPのフレームワークじゃないですけど
例えばzend frameworkのZend_Dbというクラスを使えば
http://framework.zend.com/manual/ja/zend.db.adap …
$db = Zend_Db::factory('Pdo_Mysql', $params);
$db->query();

一度$dbを作ってしまえば、あとはそれを使ってシンプル(?)な
記述で便利というか、でもそれは言語の固有の特性で
解放を自動的に行ってくれるので、このようなやり方が可能なのだと思います。
C#では当然ながら自動的に解放しないのでusingの中で処理を記述せざるえないわけですよね?
自分的にはなんとかzend frameworkじゃないですけど、それに近いクラスを
C#でも作りたい(というか慣れ親しんだイメージのまま利用したい)と思ってて、
でも言語的にさっきのオブジェクトの解放もそうですけど、
クラスやメソッドの構成に影響を与える違いもでてくるので、どうしようか悩んでいるとろこです(う~ん、うまく説明できず申し訳ないです・・・)
ちょっと漠然としすぎる質問で申し訳ないですが、もしYune-KichiさんがC#でデータベース関連のライブラリを
作成というかラップするようなクラスを作るとしたらどのように作りますか?先ほどの解放の問題も含めてですが

お礼日時:2011/10/16 01:08

IDisposable.Disposeは,大元はUnmanaged Resource,


つまりは.NET Frameworkが管理しないリソース,
ぶっちゃければメモリ以外のリソースの統一した解放手段を提供するための物です。

で,IDisposable自体は自分できっちり実装する必要があります。

class Class1 : IDisposable
{
private bool _disposed = false;
private int _i = 2;

~class1 () { Dispose(false); }

public int
{
get
{
if (_disposed) throw new ObjectDisposedException(); // 破棄されたオブジェクト
return _i;
}
set
{
if (_disposed) throw new ObjectDisposedExecption();
_i = value;
}
}

protected virtual void Dispose (bool disposing)
{
if (_disposed) return; // 多重Disposeしても安全であること
if (disposing)
{
// フィールドにあるIDisposableの解放等
}
_disposed = true;
}

public void Dispose ()
{
Dispose(true);
GC.SuppressFinalize(this); // ファイナライザの呼び出しを抑制する
}
}


で,本題ですが,トランザクションとかが関係しないのであれば,
Queryメソッドでコネクションを開き,DataSetなりにデータを読んで,コネクションを閉じるまでしてしまいます。
IDisposableなオブジェクトは,他のオブジェクト以上に所有権を考えないといけないため,
できるだけIDisposableなオブジェクトはusingで囲っておきたいです。

ASP.NETだとまた別の解があって,
HttpContext.Current.ApplicationInstance.EndRequestイベントで解放するように構築してしまう,というのも手です。
ASP.NETだと短い時間で処理が終わるので,終了イベントを捕まえてそこで解放しても十分,という判断です。
# 実際にこういう作りにしたことがあります。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
なるほど、自分のやり方ではちゃんと実装してなかったわけですね・・・
参考になりました。

やはりそうですか、usingで囲うしかないですかね。。
というより、考え方を変えて設計する必要がありそうですね。
これからも自分なりに作っていきたいと思います。

お礼日時:2011/10/16 14:40

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!