- ベストアンサー
C#初心者のためのオブジェクトでの値渡し方法
- C#初心者の方へ、オブジェクトでの値の渡し方を解説します。
- ValueChangingメソッドを使用して、refを使わずに値を渡す方法を紹介します。
- 値の渡し方をスッキリさせることで、コードの見やすさや保守性を向上させることができます。
- みんなの回答 (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 参照型はポインタのポインタを渡していますが。 そもそも,気にする必要があるほどの速度の違いなんてまずないと思いますが。
その他の回答 (1)
- Yune-Kichi
- ベストアンサー率74% (465/626)
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; // これに相当する部分は抜け? }
お礼
確かに後からSampleに代入し直すことも考えていたのです。 ただ、この方法では (1):e.NewValueが変化されてもintのような値型の場合、戻ってくるまではSampleが変化しないので、マルチスレッドにした場合、不都合が出る (2):何らかの理由で代入すらしたくない場合も代入してしまう (これは~Argsのフィールドにboolを追加すればOkでした) (3):(1)をできるだけ避けるならChangingイベントと、Changedイベントに分けて行う ⇒ 場合によってはChangingイベントで算出された結果をChangedイベントで用いる場合、そのためだけに一旦フィールド変数なんかにおいておかないといけないのでなんとなく嫌 (4):ポインタを渡す ⇒ intのサイズなら値渡しのほうが速いのでなんとなく嫌 だったのですが、値型を一旦他の変数に代入して持っていっている時点でref参照と同等になんてならないですよね。 結局自己解決(?)しました。 ありがとうございます。
お礼
そうですね。ありがとうございます。 細やかなご指導ありがとうございました。