C#初心者のためのオブジェクトでの値渡し方法

このQ&Aのポイント
  • C#初心者の方へ、オブジェクトでの値の渡し方を解説します。
  • ValueChangingメソッドを使用して、refを使わずに値を渡す方法を紹介します。
  • 値の渡し方をスッキリさせることで、コードの見やすさや保守性を向上させることができます。
回答を見る
  • ベストアンサー

c# refで渡すようにオブジェクトで渡したい

こんにちは。c#初心者です。 以下のコードで、 ValueChanging(this, ref Sample, ref value); と、しているところを ValueChanging(this, e)のようにひとつのオブジェクトにまとめて同じ結果にするようなことはできるのでしょうか? わかる方いらっしゃいましたら教えてください。 もし、無理なら無理と言っていただけるとありがたいです。 -------------------------------------------------------- //イベント類 public delegate void ValueChangingHandler(object sender, ref oldValue, ref newValue); public event ValueChangingHandler ValueChanging; //フィールド&プロパティ protected int Sample; public int Value { get { return Sample; } set { ValueChanging(this, ref Sample, ref value); } } --------------------------------------------------------

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

懸念事項に,使う側が対処すべき事まで含んでいる気がしますが……。 > (1):e.NewValueが変化されてもintのような値型の場合、戻ってくるまではSampleが変化しないので、マルチスレッドにした場合、不都合が出る Changingイベント発生の段階では,変更前のイベントなので古い値が読める方が正解だと思います。 Changedイベント発生の段階では,変更後のイベントなので新しい値が読める必要がありますが。 マルチスレッドで変更後の値が読みたいのであれば,ロックをかける必要があります。 ただし,ロックをかけるのは使う側の責務であって,ValueChangingイベントやValueChangedイベントを用意する側の責務では無いです。 # Valueのプロパティメソッドの中でロックを掛けると,不要な場合にもロックしたり,デッドロックにを発生させる可能性があるため。 なお,そもそもChangingイベントで値を新しくするなら,キャンセル可能にしてキャンセル + 新しい値の代入で処理した方がよいと思います。 > (2):何らかの理由で代入すらしたくない場合も代入してしまう > (これは~Argsのフィールドにboolを追加すればOkでした) System.ComponentModel.CancelEventArgsを基本クラスにしてCancelプロパティを利用するのが統一的で良いでしょう。 # 変更の前通知を取りやめるのにCancelEventArgsを使うのは,MSのデザインガイドラインに沿います。 public class ValueChangingEventArgs : CancelEventArgs {  public ValueChangingEventArgs (int oldValue, int newValue) : base(false)  {   OldValue = oldValue;   NewValue = newValue;  }  public int OldValue { get; private set; }  public int NewValue { get; private set; } } public class ValueChangedEventArgs : EventArgs {  public ValueChangingEventArgs (int oldValue, int newValue)  {   OldValue = oldValue;   NewValue = newValue;  }  public int OldValue { get; private set; }  public int NewValue { get; private set; } } public event EventHandler<ValueChangingEventArgs> ValueChanging; public event EventHandler<ValueChangingEventArgs> ValueChanged; protected virtual void OnValueChanging (ValueChangingEventArgs e) {  if (ValueChanging != null) ValueChanging(this, e); } protected virtual void OnValueChanged (ValueChangedEventArgs e) {  if (ValueChanged != null) ValueChanged(this, e); } public int Value {  get { return Sample; }  set  {   var e = new ValueChangingEventArgs(Sample, value);   OnValueChanging(e);   if (e.Cancel) return;   Sample = value;   OnValueChanged(new ValueChangedEventArgs(Sample, value));  } } // 変更を取りやめたい場合 private void object_ValueChanging (object sender, ValueChangingEventArgs e) {  if (!IsValid(e.NewValue))  {   e.Cancel = true;   return;  } } // 変更後の値を修正したい場合 private void object_ValueChanging (object sender, ValueChangingEventArgs e) {  if (e.NewValue % 2 == 1)  {   e.Cancel = true;   ((Foo)sender).Value = e.NewValue - 1;   return;  } } > (3):(1)をできるだけ避けるならChangingイベントと、Changedイベントに分けて行う ⇒ 場合によってはChangingイベントで算出された結果をChangedイベントで用いる場合、そのためだけに一旦フィールド変数なんかにおいておかないといけないのでなんとなく嫌 これは明らかに使う側で対処すべき (使う側の問題なので)。 > (4):ポインタを渡す ⇒ intのサイズなら値渡しのほうが速いのでなんとなく嫌 ref 値型はポインタを渡していますよ。 ref 参照型はポインタのポインタを渡していますが。 そもそも,気にする必要があるほどの速度の違いなんてまずないと思いますが。

koumei000
質問者

お礼

そうですね。ありがとうございます。 細やかなご指導ありがとうございました。

その他の回答 (1)

回答No.1

OldValueとNewValueというふたつのプロパティ(or フィールド)を持つクラスを用意すれば良いです。 INotifyCollectionChanged.CollectionChanged イベントで使われるNotifyCollectionChangedEventArgs クラスなどが参考になるかと。 以下,インデントをU+3000で行っているので,修正して使って下さい。 public class ValueChangingEventArgs : EventArgs {  public ValueChangingEventArgs (int oldValue, int newValue)  {   OldValue = oldValue;   NewValue = newValue;  }  public int OldValue { get; set; }  public int NewValue { get; set; } } > public event ValueChangingHandler ValueChanging; public event EventHandler<ValueChangingEventArgs> ValueChanging; > set { ValueChanging(this, ref Sample, ref value); } set {  var e = new ValueChangingEventArgs(Sample, value);  if (ValueChanging != null) ValueChanging(this, e);  Sample = e.NewValue; // これに相当する部分は抜け? }

koumei000
質問者

お礼

確かに後からSampleに代入し直すことも考えていたのです。 ただ、この方法では (1):e.NewValueが変化されてもintのような値型の場合、戻ってくるまではSampleが変化しないので、マルチスレッドにした場合、不都合が出る (2):何らかの理由で代入すらしたくない場合も代入してしまう (これは~Argsのフィールドにboolを追加すればOkでした) (3):(1)をできるだけ避けるならChangingイベントと、Changedイベントに分けて行う ⇒ 場合によってはChangingイベントで算出された結果をChangedイベントで用いる場合、そのためだけに一旦フィールド変数なんかにおいておかないといけないのでなんとなく嫌 (4):ポインタを渡す ⇒ intのサイズなら値渡しのほうが速いのでなんとなく嫌 だったのですが、値型を一旦他の変数に代入して持っていっている時点でref参照と同等になんてならないですよね。 結局自己解決(?)しました。 ありがとうございます。

関連するQ&A

  • オブジェクトの参照を返す関数の扱い

    オブジェクトの作成と関数との関係を勉強していますが参照の使い方に関して質問させていただきたいと思います。 以下のような簡単なプログラムを作りました。 myclass.cpp myclass.h で記述されたクラスmyclassは整数を一つ持ち、show_value関数でその整数を表示し、say_heyで"hey"という文字を出力するというものです。 このクラスを利用するプログラムとしてmain.cppをつくりました。このなかには2つの関数が使われます。 -オブジェクトをつくりそれをオブジェクトとして返す関数(return_obj)   -オブジェクトをつくりそれの参照を返す関数(return_ref_obj) これらの関数を用いてオブジェクトをつくり、そのオブジェクトを戻り値としてmainのなかでオブジェクトのshow_value関数で保持する整数を表示する、というものです。 myclass.h-------------------------------- class Myclass{ public: int my_int; Myclass(); ~Myclass(); void show_value(); void say_hey(); }; myclass.cpp---------------------------------- #include "myclass.h" #include <iostream> using namespace std; Myclass::Myclass(){}; Myclass::~Myclass(){}; void Myclass::show_value(){ printf("%d\n", my_int); } void Myclass::say_hey(){ printf("hey\n"); } main.cpp------------ #include <iostream> #include "myclass.h" using namespace std; //オブジェクトをつくりそれをオブジェクトとして返す関数(return_obj)   Myclass return_obj(int int_in){ Myclass myobject; myobject.my_int = int_in; //引数をオブジェクトのmy_intに渡す return myobject; } //オブジェクトをつくりそれの参照を返す関数(return_ref_obj) Myclass& return_ref_obj(int int_in2){ Myclass myobject; myobject.my_int = int_in2;   //引数をオブジェクトのmy_intに渡す Myclass& ref_of_myclass = myobject; return ref_of_myclass; } void main(){ Myclass returned_obj = return_obj(1); //関数に1を渡し、1を保持するオブジェクトを作成 returned_obj.show_value(); //整数(1)表示 returned_obj.say_hey(); Myclass& ref_obj = return_ref_obj(2); //関数に2を渡し、2を保持するオブジェクトを作成し参照として受け取る ref_obj.show_value(); //整数(2)表示 ref_obj.say_hey(); } プログラムを実行した出力------------------- 1 hey -858993460 hey このようにオブジェクト自体を返してコピーしたもの(return_obj使用)はshow_valueでただしくオブジェクトに保持された数が表示されますが 参照でオブジェクトを返したもの(return_ref_obj使用)はアドレスのような数列が表示されます。 質問A これはオブジェクトの参照を返す関数(return_ref_obj)でオブジェクトを作成しても、そのオブジェクトが関数の中でのみ実在しており、関数がおわるとその実体がなくなるためではないかと解釈しているのですがそれで正しいでしょうか? 質問B 関数でオブジェクトを作成してそれをプログラム本体に渡すときはreturn_objのようにオブジェクト自体をコピーしなければ参照などで渡すことはできないのでしょうか? クラスと参照自体勉強を始めたばかりで色々と初歩的な間違いもあるとは思いますが、よろしくお願いします

  • C#でFormオブジェクトのイベントについて

    C#において Formオブジェクトに付加したコントロールのMouseWheelイベントについて質問です。 ある特定のコントロールにMouseWheelイベントを付加してホイールボタンの動きをつけたのですが、どうも特定のコントロールだけでなく、Formオブジェクト全体にWheelのイベントが付加されてしまっているようです。以下のコードがそれです。 using System; using System.Windows.Forms; using System.IO; using System.Drawing; //実行クラス public class MainClass{ //実行メソッド public static void Main(string [] args){ NewForm formObj =new NewForm(); formObj.SetLabel(); Application.EnableVisualStyles(); Application.Run(formObj); } } //テスト用Form拡張クラス public class NewForm :Form{ public Label labelObj; public NewForm(){ this.Width = 600; this.Height = 600; this.Text ="wheel test"; } public void SetLabel(){ this.labelObj =new Label(); this.labelObj.Width=200; this.labelObj.Height =50; this.labelObj.Location = new Point(10,10); this.labelObj.Text ="ホイールのテスト"; this.labelObj.BackColor =Color.Red; this.Controls.Add(this.labelObj); //delegate this.labelObj.MouseEnter += new EventHandler(this.EnterMethod); this.labelObj.MouseWheel += new MouseEventHandler(this.WheelMethod); } //コントロールに追加したラベルにフォーカスした時のメソッド public void EnterMethod(Object sender,EventArgs e){ this.labelObj.Focus(); } public void WheelMethod(Object sender,MouseEventArgs e){ MessageBox.Show(e.Delta.ToString()); } } 上記のコードだと、どうしても横幅200縦50のラベルコントロールの外側でもそのWheelイベントが 発生してしまうようです。 これを回避するために無理やり以下のようにソースを改変しました。 public void WheelMethod(Object sender,MouseEventArgs e){ //ディスプレイ基準ではなく、Formオブジェクト基準の座標の取得 Point tempObj = this.labelObj.PointToClient(Cursor.Position); int tempX = tempObj.X; int tempY =tempObj.Y; if ( (tempX > 0) && (tempX < this.labelObj.Width) && (tempY > 0) && (tempY < this.labelObj.Height) ){ MessageBox.Show(tempObj.X.ToString()); MessageBox.Show(tempObj.Y.ToString()); MessageBox.Show(e.Delta.ToString()); } } マウスカーソルの位置が、特定のラベルの範囲内でのみ動くようにしたのですが これのやりかたがベターというかC#上では定石なのでしょうか? そもそも、なぜラベルコントロールに対してMouseWheelイベントをつけているのに ラベルのある座標以外で、Wheelイベントが発生してしまうのでしょうか? よろしくご教授ください。 ちなみにC#の質問ってこのASPの項目でもいいんですかね? C,C++とは色が違うし、 そのほかのプログラミングのほうがよいのでしょうか?

  • c# イベントハンドラ 統一

    trackBarを複数配置しているのですが、そのtrackBarごとに private void trackBar1_Scroll(object sender, EventArgs e) { previewcolor(this.trackBar1.Value,'赤'); } private void trackBar2_Scroll(object sender, EventArgs e) { previewcolor(this.trackBar1.Value, '青'); } private void trackBar3_Scroll(object sender, EventArgs e) { previewcolor(this.trackBar1.Value, '緑'); } private void trackBar4_Scroll(object sender, EventArgs e) { previewcolor(this.trackBar1.Value, '透'); } こんな風に別々に記述する形になって非常にスッキリしないです。 これをジェネリクス?やデリゲート?などを使ってすっきりできないのでしょうか? visual stdio c# 2008を使ってます。 宜しくお願いします。

  • C#でオブジェクトの有無を取得する

    オブジェクトが作られている時と作られていない時で処理をわけたいのですがうまくいきません。 この書き方の何がまずいのでしょうか? namespace オブジェクト検索 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Control c = Controls["form2"]; if (c != null) { ((TextBox)c).Text += "*"; } } private void button2_Click(object sender, EventArgs e) { Form2 form2 = new Form2(); form2.Show(); Application.DoEvents(); } } } ボタン2を押した時に新しいフォームが立ち上がり、そのフォームがあるときは*が出るようにしたいです。

  • デリゲートで子から親フォームのメソッド実行について

    お世話になります。 .NET C#で開発をしております初心者です。 下記の様なデリゲートで子フォームから親フォームのメソッドを実行 させようとしています。 すると、parent2.MeisaiSyutoku(); の部分で エラーが発生してしまいます。 (エラー内容:NullReferenceExceptionはハンドルされませんでした。        オブジェクト参照がオブジェクト インスタンスに設定されていません。) 同じ子フォームで(親フォームは違いますが)、もう1つデリゲートをやっているのですが それも何か悪かったりするでしょうか。 下記のデリゲートで、デリゲート(2)はちゃんと実行されますが、デリゲート(1)の方でエラー となってしまいます。 どなたかご親切な方、違う方法でも構いませんので、子フォームから 親フォームのメソッドを実行する方法を教えて下さいます様、 宜しくお願い致します。      記 デリゲート(1) 子フォーム側 parent2; //親フォームの参照 public void setOya2(案内書 parent) { this.parent2 = parent; //親フォームから参照を受け取って保持する } private void button2_Click(object sender, EventArgs e) { ~イベント処理~ parent2.MeisaiSyutoku(); //案内書.csのメソッド実行 } 親フォーム側 private void button4_Click(object sender, EventArgs e) { Global.openMOTO = 1; 台帳 n台帳 = new 台帳(); n台帳.setOya2(this); n台帳.Show(); } private delegate void ctlFromChild(); public void MeisaiSyutoku() { new Thread(new ThreadStart(delegate { Invoke((ctlFromChild)delegate { Get_MeisaiSyutoku(); }); })).Start(); } デリゲート(2) 子フォーム側 送り状作成 parent1; //親フォームの参照 public void setOya1(送り状作成 parent) { this.parent1 = parent; //親フォームから参照を受け取って保持する } private void fpSpread1_CellDoubleClick(object sender, CellClickEventArgs e) { ~イベント処理~ parent1.changeTextBox4(zOtodokeSaki);         } 親フォーム側 private void button1_Click(object sender, EventArgs e) { Global.openMOTO = 0; 台帳 n台帳 = new 台帳(); n台帳.setOya1(this); n台帳.Show(); } private delegate void ctlFromChild(); public void changeTextBox4(string str) { new Thread(new ThreadStart(delegate { Invoke((ctlFromChild)delegate { this.textBox4.Text = str; }); })).Start(); }

  • C#でオブジェクトの配列を使う

    開発環境はMicrofoft visual stdio 2005 で開発言語はC#を使い、Windousアプリケーションでプログラミングしているのですが オブジェクトを配列でせんげんし、オブジェクトのメンバを使うとエラーが出てしまいます。ソースコードで説明すると using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace WindowsApplication1 {   public partial class Form1 : Form   {     public class car     {       int a;       public void set(int i)       {         a = i;       }     }     public Form1()     {       InitializeComponent();     }     private void label1_Click(object sender, EventArgs e)     {       car[] bike = new car[1];       bike[0].set(1);//ここでひっかかる     }   } } これをじっこうするとデバックは通るのですが、label1_Clickイベントが発生すると、「NULLの場所を参照しています」という意味のエラーが出てしまいます。 C#はほとんど初心者なので、初歩的なミスだとおもうんですが、 よろしければ御回答をお願いします。

  • C# panelのなかのpctureBox

    panel1のなかのpictureBox1 QNo.8077421の続き http://okwave.jp/qa8077421.html Form1にPicturBox1をはりつけてダブルクリックでイベントハンドラを作る Properties.Resources.IconH;は、*.bmp private void pictureBox1_Click(object sender, EventArgs e) { pictureBox1.Image = Properties.Resources.IconH; } これは、OK private void pictureBox1_Click(object sender, EventArgs e) { int a = 1; Control c = this.Controls["PictureBox"+a.ToString()]; ((PictureBox)c).Image = Properties.Resources.IconH; } これも、OK ここからがうまくいきません。 panel1を貼り付けPicturBox1をpanel1のなかに。 pictureBox1.Image = Properties.Resources.IconH; これは、OK ここからです。 int a = 1; Control c = this.Controls["PictureBox"+a.ToString()]; ((PictureBox)c).Image = Properties.Resources.IconH; これは、 NG NullReferenceException はハンドルされませんでした。 オブジェクト参照がオブジェクト インスタンスに設定されていません。 ((PictureBox)c).Image = Properties.Resources.IconH;これ これをつかいたい。 アドバイスをお願いできませんでしょうか。

  • C# DataGridView列カスタマイズ

    C#でDataGridViewの列にNumericUpDownにしたくカスタマイズを行っています。 http://msdn.microsoft.com/ja-jp/library/7tas5c80(VS.80).aspx こちらのサイトを参考にし以下のソースになりました。 public class NumericUpDownColumn : DataGridViewColumn { public NumericUpDownColumn() : base(new NumericUpDownCell()) { } public override DataGridViewCell CellTemplate { get { return base.CellTemplate;   } set { base.CellTemplate = value; } } } public class NumericUpDownCell : DataGridViewTextBoxCell { public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) { base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle); NumericUpDownCellEditingControl ctl = DataGridView.EditingControl as NumericUpDownCellEditingControl; if (this.Value != null) ctl.Value = decimal.Parse(this.Value.ToString()); } public override Type EditType { get { return typeof(NumericUpDownCellEditingControl); } } public override Type ValueType { get { return typeof(decimal); } } public override object DefaultNewRowValue { get { return (decimal)0; } } } class NumericUpDownCellEditingControl : NumericUpDown, IDataGridViewEditingControl { DataGridView dataGridView; private bool valueChanged = false; int rowIndex; public object EditingControlFormattedValue { get { return this.Value; } set { String newValue = value as String; if (newValue != null) { this.Value = decimal.Parse(newValue); } } } public object GetEditingControlFormattedValue( DataGridViewDataErrorContexts context) { return EditingControlFormattedValue; } public void ApplyCellStyleToEditingControl( DataGridViewCellStyle dataGridViewCellStyle) { this.Font = dataGridViewCellStyle.Font; } public int EditingControlRowIndex { get { return rowIndex; } set { rowIndex = value; } } public bool EditingControlWantsInputKey( Keys key, bool dataGridViewWantsInputKey) { switch (key & Keys.KeyCode) { case Keys.Left: case Keys.Up: case Keys.Down: case Keys.Right: return true; default: return false; } } public bool RepositionEditingControlOnValueChange { get { return false; } } public DataGridView EditingControlDataGridView { get { return dataGridView; } set { dataGridView = value; } } public bool EditingControlValueChanged { get { return valueChanged; } set { valueChanged = value; } } public Cursor EditingPanelCursor { get { return base.Cursor; } } protected override void OnValueChanged(EventArgs eventargs) { valueChanged = true; this.EditingControlDataGridView.NotifyCurrentCellDirty(true); base.OnValueChanged(eventargs); } } } デザイン画面で列を追加し実行。グリッド上のNumericUpDownで数値を変更し 違うセルを選択すると「セルのフォーマットされた値に間違った型が指定されていますと例外がでます。場所はDataGridViewCellのParseFormattedValueでした。 どのようにすれば例外がでなくなるでしょうか。ちょっと文字数の関係上色々はしょってしまってます。 よろしくお願いします。

  • 整数値を任意のオブジェクトを格納できるスタッククラスに格納する方法

    class ObjStack { private Object[] mBuf; private int mSP; public ObjStack(int size) { mSP = 0; mBuf = new Object[size]; } public int getSize() { return mBuf.length; } public int getNum() { return mSP; } public boolean isFull() { return mSP == mBuf.length; } public boolean isEmpty() { return mSP == 0; } public void push(Object x) { if (!isFull()) { mBuf[mSP++] = x; } } public Object top() { Object obj = null; if (!isEmpty()) { obj = mBuf[mSP-1]; } return obj; } public Object pop() { Object obj = null; if (!isEmpty()) { obj = mBuf[--mSP]; } return obj; } } 上のプログラムは任意のオブジェクトを格納できるスタッククラスです。 int型の値はオブジェクトではないので、このObjyStackクラスに格納することができない。 整数値をこのObjStackクラスに格納する方法を教えて下さい。

    • ベストアンサー
    • Java
  • 「オブジェクト志向」の考えかたで質問します。

    「オブジェクト志向」の考えかたで質問します。 いろいろと調べると、 ・繼承 ・カプセル化 ・ポリモーフィズム を総称したのが、「オブジェクト」志向と理解しています。 このとき、 たとえば、 指定するクラスの生徒の情報をとりだすようなソースをつくりたい。 仮に、以下をかんがえてみました。 DB処理は、省いてます。 //実行DAOクラス public class StudentDAO extends StudentDBAccessor{ //指定するクラスに属する生徒をとりだす public List getStudentList(int classNumber){ return super.getStudentList(); } //sql文生成 protected String createSqlSelectStudentList(){ StringBuffer sb = new StringBuffer(); return sb.toString(); } //キーワードを設定 public void setDataSqlStudentList(){ } } public abstract StudentDBAccessor extends DBConnector{ protected List getStudentList(){   //DBそうさ } protected abstract String createSqlStudentList(); protected void setDataSqlStudentList(int classNumber); } //DB接続クラス public class DBConnector{   //省略 } //Beanクラス public class StudentFormBean{ private int studentNumber; private String studentName; public void setStudentNumber(int number){ this.studentNumber = number; } public int getStudentNumber(){ return studentNumber; } } よろしくおねがいします。

    • ベストアンサー
    • Java

専門家に質問してみよう