- PR -

C#にてオブジェクトの参照渡しが出来ないです。

投稿者投稿内容
一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2005-07-06 13:57
引用:

とよはらさんの書き込み (2005-07-06 13:41) より:
今回の様にオブジェクトが大きくなる可能性がある場合は、
個人的にはメモリの確保のしかたからref のほうが良いと思っております。



ふっふっふ、参照型値渡しをしてもインスタンスが別に作られるわけではありませんよ。

DataTableへの参照を返り値で返すのがスマートですね。
とよはら
常連さん
会議室デビュー日: 2003/05/09
投稿数: 21
投稿日時: 2005-07-06 15:42
>ふっふっふ、参照型は値渡しをしてもインスタンスが別に作られるわけではありませんよ。

すみません。初心者なので良く理解出来ていないかもしれません。
参照型を引数で渡すと参照渡しになるとありましたので、
値渡しは出来ないのではないのでしょうか。

今回は空のオブジェクトを渡しているので、
初期化を必要としない「out」修飾子と書き込むことにしようかと考えています。

>DataTableへの参照を返り値で返すのがスマートですね。

こちらと同じ意味でしょうか。


<呼び出し側>
DataTable table;
eBiz_CommonComponent.DBManager obj = new DBManager();
obj.getDataTable("server=***;database=***;user id=***;password=***;packet size=4096",30,"SELECT * FROM trn_order_head",out<space>table);

<呼ばれ側>
public class DBManager
{
public void getDataTable(string dbConnString,
int cmdTimeOut,
string sql,
out<space>System.Data.DataTable dataTable)

Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-07-06 16:05
参照型を値渡しすると(デフォルトは値渡しです)、参照(要はアドレスの値)がコピーされて渡されます。
ヒープに確保されたインスタンスそのものを渡すわけではありません。
返値も値渡しですから、参照型を返すと言うことはそのインスタンスの参照(アドレス)のコピーを返すと言うことです。
つまり参照型をメソッド間でやりとりすると、動く(コピーが発生する)のは参照に使われる数バイトのみということです。

さて基本に立ち返って、DBManager.GetDataTableメソッドを考えた場合、
機能としては接続文字列を与えたらDataTableオブジェクトが返ってくる、と言うものですね。
これは明らかに返値として実装するのが自然でしょう。
餅宮餅吉
ベテラン
会議室デビュー日: 2005/03/04
投稿数: 57
お住まい・勤務地: 月餅のうまい店の隣
投稿日時: 2005-07-06 16:06
引用:

とよはらさんの書き込み (2005-07-06 15:42) より:

すみません。初心者なので良く理解出来ていないかもしれません。
参照型を引数で渡すと参照渡しになるとありましたので、
値渡しは出来ないのではないのでしょうか。


 普通に(refなしで)渡すと値渡しになると思うデスケド(違ったかしら。

引用:

今回は空のオブジェクトを渡しているので、
初期化を必要としない「out」修飾子と書き込むことにしようかと考えています。

>DataTableへの参照を返り値で返すのがスマートですね。

こちらと同じ意味でしょうか。



 戻り値で返したらダメなんでしょうか?

<呼び出し側>
コード:
DataTable table = new DBManager().GetDataTable(
                       "server=***;database=***;user id=***;password=***;packet size=4096",
                        30,
                        "SELECT * FROM trn_order_head"); 



<呼ばれる側>
コード:
public class DBManager 
{ 
    public DataTable GetDataTable(string dbConnString, int cmdTimeOut, string sql) {
         return DataTableのインスタンス;
    }
}

NAL-6295
ぬし
会議室デビュー日: 2003/01/26
投稿数: 966
お住まい・勤務地: 東京
投稿日時: 2005-07-06 16:32
NAL-6295です。

参照型とrefキーワードの関係について混乱しているようなので、参考までに・・・
[C#]参照型にrefキーワードがついている時とついていない時の違い

_________________
「伝える」とは「人に云う」と書く。
http://d.hatena.ne.jp/NAL-6295/
とよはら
常連さん
会議室デビュー日: 2003/05/09
投稿数: 21
投稿日時: 2005-07-06 19:42
みなさん、ありがとうございます。
餅宮餅喜さん、サンプルありがとうございます。。

>参照型を値渡しすると(デフォルトは値渡しです)、
>参照(要はアドレスの値)がコピーされて渡されます。
>ヒープに確保されたインスタンスそのものを渡すわけではありません。
>返値も値渡しですから、参照型を返すと言うことはそのインスタンスの参照
>(アドレス)のコピーを返すと言うことです。
>つまり参照型をメソッド間でやりとりすると、動く(コピーが発生する)のは参照に使>われる数バイトのみということです。

納得いたしました。。


>さて基本に立ち返って、DBManager.GetDataTableメソッドを考えた場合、
>機能としては接続文字列を与えたらDataTableオブジェクトが返ってくる、
>と言うものですね。これは明らかに返値として実装するのが自然でしょう。

確かにそちらの方がシンプルなのですが、
この戻り値で返すか、パラメータで返すかを決める基準がよく理解出来ていないようです。すみません。本題から離れておりますが宜しければご教授お願いします。


Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-07-06 20:06
 解決した後でナンですが、私が書き込みできるのは、基本的に夜だけなので。
# 全部書かれてるんだよなぁ。(--


> 参照渡しが必要な場面とは思えませんけど... by じゃんぬねっと さん
sqlAdapter.Fill(dataTable); でいいですよね。DataSetを宣言する必要もありませんし、その辺りのif文も、不要になります。
# 使う前に調べましょう > とよはら さん


> パラメータは普通、値渡しだと思います。 by 餅宮餅喜 さん
> ref を指定してみてはいかが by 117 さん
> >"値型-参照型" と "値渡し-参照渡し" by 一郎 さん
引用:

以下の様にTableオブジェクトを参照渡ししても中身が渡す前と変わりません。
またC#はオブジェクト型は全て参照渡しとなると認識しております。


 参照渡しではなく、参照型です。メソッドへの引数は、参照を値渡しします。
 引数tableをメソッドの“出力”として扱いたい場合、outで修飾します。このとき、“出力”されるのですから、メソッドの呼び出し側でnewする必要はありません。


> あと、finallyとか使ったほうが良いです。 by 餅宮餅喜 さん
 あと、throwしたときにどういう流れになるか、きちんと把握してくださいね。throwすると、それ以降の命令は実行されません。ただし、finallyブロックは例外です。
コード:
out修飾子を使う場合:
<呼び出し側> 
	DataTable table;
	eBiz_CommonComponent.DBManager obj = new DBManager(); 
	obj.getDataTable(..., out table);


<呼ばれ側>
public void getDataTable(string dbConnString,
int cmdTimeOut,
string sql,
out System.Data.DataTable dataTable) {
	System.Data.SqlClient.SqlConnection sqlConn = null;
	System.Data.SqlClient.SqlDataAdapter sqlAdapter = null;
	System.Data.DataSet ds = null;
	
	try { 
		sqlConn = new System.Data.SqlClient.SqlConnection(dbConnString);
		sqlConn.Open();
		sqlAdapter = new System.Data.SqlClient.SqlDataAdapter(sql,sqlConn);
		ds = new System.Data.DataSet(); 
		sqlAdapter.SelectCommand.CommandTimeout = cmdTimeOut;
		sqlAdapter.Fill(ds); 

	} finally {
		if (sqlConn != null) sql.Conn.Close();
		if (sqlAdapter != null) sqlAdapter.Dispose();
	}

	if(ds.Tables.Count == 1) {
		dataTable = ds.Tables[0];
	} else { 
		throw new System.Exception("対象DataTableが一つではありません。");
	}
}

修飾子を使わない場合:
<呼び出し側> 
	DataTable table = new DataTable("tableName");
	eBiz_CommonComponent.DBManager obj = new DBManager(); 
	obj.getDataTable(..., table);


<呼ばれ側>
public void getDataTable(string dbConnString,
int cmdTimeOut,
string sql,
System.Data.DataTable dataTable) {
	System.Data.SqlClient.SqlConnection sqlConn = null;
	System.Data.SqlClient.SqlDataAdapter sqlAdapter = null;
	
	try { 
		sqlConn = new System.Data.SqlClient.SqlConnection(dbConnString);
		sqlConn.Open();
		sqlAdapter = new System.Data.SqlClient.SqlDataAdapter(sql,sqlConn);
		sqlAdapter.SelectCommand.CommandTimeout = cmdTimeOut;
		sqlAdapter.Fill(dataTable);

	} finally {
		if (sqlConn != null) sql.Conn.Close();
		if (sqlAdapter != null) sqlAdapter.Dispose();
	}
}

返値とする場合: by Hongliang さん
<呼び出し側> 
	eBiz_CommonComponent.DBManager obj = new DBManager(); 
	DataTable table = obj.getDataTable(...);


<呼ばれ側>
public DataTable getDataTable(string dbConnString,
int cmdTimeOut,
string sql) {,
	System.Data.SqlClient.SqlConnection sqlConn = null;
	System.Data.SqlClient.SqlDataAdapter sqlAdapter = null;
	System.Data.DataTable tbl = new DataTable("TableName");

	try { 
		sqlConn = new System.Data.SqlClient.SqlConnection(dbConnString);
		sqlConn.Open();
		sqlAdapter = new System.Data.SqlClient.SqlDataAdapter(sql,sqlConn);
		sqlAdapter.SelectCommand.CommandTimeout = cmdTimeOut;
		sqlAdapter.Fill(tbl); 

	} finally {
		if (sqlConn != null) sql.Conn.Close();
		if (sqlAdapter != null) sqlAdapter.Dispose();
	}

	return tbl;
}



 このコード、まだまだリファクタリングできます。

 接続文字列を渡すのではなく、接続を渡すようにしましょう。おそらく、接続文字列はハードコーディングしていないとは思いますが、ハードコーディングしているなら最悪です。接続インスタンスを返す、静的なメソッドを用意するようにしましょう。
 この例のように作られているとしたら、ユーザ入力をSQL文に展開して引き渡していることが予想されます。それはSQLインジェクションなどの攻撃、あるいは不測の事故によるSQLコードの混入に大変弱い作りといえます。パラメータ渡しをするようにしましょう。
コード:
public class ConnectionCreator {
	public static SqlConnection CreateConnection() {
		// こうしておけば、接続先が変わってもここだけ変更すればよい。
		// また、接続文字列を外部から供給するように変更しても、
		// ここだけ変更すればよい。
		return new SqlConnection("server=***...");
	}
}



_________________
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-07-06 20:11
引用:

とよはらさんの書き込み (2005-07-06 19:42) より:

確かにそちらの方がシンプルなのですが、
この戻り値で返すか、パラメータで返すかを決める基準がよく理解出来ていないようです。すみません。本題から離れておりますが宜しければご教授お願いします。


 簡単です。メソッドに引き渡して使われるのか、メソッドの中で作られるのか、ということです。

 もっとも、返ってくるものが複数ある場合は、いくつかをrefやoutで修飾しますが、この場合、返ってくる(メソッド内で作って戻す/メソッドの中では使わない)のは1つですから、それだけを戻り値にすればいいです。
_________________

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