質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

4回答

12186閲覧

イベントの無限ループを回避する方法(詳細は中をご覧ください。)

Ryzna

総合スコア85

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2015/04/14 06:12

編集2015/04/16 01:54

毎度お世話になっております。Ryznaと申します。

タイトルに一言で表せなかったので改めて質問内容です。

環境:
言語:C#
.net framework:4.5.2
実装したいところ:WCFサービスから参照されるDLL内

質問内容:
メソッドMを実行するとイベントEが発生するような実装のクラスAがあるとします。
クラスAから2つのインスタンス(それぞれインスタンス1,インスタンス2)を作成します。
インスタンス1.イベントEが発生した場合にインスタンス2.メソッドMを実行し、逆に、
インスタンス2.イベントEが発生した場合にインスタンス1.メソッドMを実行したいとします。
Eイベントから実行されたMメソッドが発生させるEイベントは処理したくないとします。

当然このままでは相互にイベントが発生し続けて無限ループになってしまうのでこれを回避するためにどのような実装をするべきなのでしょうか。

補足と現状での考え:
このプログラムはマルチスレッドで実行されており、当然別クラスからも呼ばれることも想定しておりますので、とりあえずロック内でイベントハンドラを一時的に回避するor「イベントを処理しない」フラグによる判定は思いついているのですがこれでいいのかなぁと自信がない次第です。

Aクラスは自前のクラスですので手を入れられない訳ではないのですが、既にリリースされているものであり出来れば手を入れずに済めばなぁと考えております。
そもそも根本の実装を修正しなきゃダメという情報でも構いませんので皆様のお知恵を拝借できればと思います。

よろしくお願いします。
以上

追記:
4/15 19:30 頃までにご提案頂いた方法の中からベストアンサーを決定させていただきます。
それ以降も継続してご意見ご提案の募集は継続させて頂きます。よろしくお願いします。

追記2:
4/15 20:00の時点でご提案いただいていた3つの方法のうち、
・既存コードに手を加えない
・一連のフローの中で切り替えが行える
・クラスAと異なる構造でも適用できる
の3つの点でtorakichiさんの案をベストアンサーとさせていただきました。
もっといい方法があるよ!というご提案は引き続き募集させていただきますのでぜひご投稿いただければと思います。

追記3:
以下サンプルです。

lang

1// 既存のクラス 2// 既にリリース済み別案件で使用中なので出来れば手を入れたくない。入れちゃダメというわけでもない 3public class ClassA 4{ 5 public event EventHandler EventE; 6 7 public void MethodM() 8 { 9 var e = new EventArgs(); 10 11 // MethodM内のいろいろ処理 12 13 OnEventE(e); 14 } 15 16 protected virtual OnEventE(EventArgs e) 17 { 18 // OnEventE内のいろいろ処理 19 20 if (EventE != null) 21 EventE(this, e); 22 } 23} 24 25// ものすごく簡略化した新規コード 26public class Hoge 27{ 28 private ClassA _instance1; 29 private ClassA _instance2; 30 31 public Hoge() 32 { 33 _instance1 = new ClassA(); 34 _instance2 = new ClassA(); 35 } 36 37 private void Instance1_EventE(object obj, EventArgs e) 38 { 39 // インスタンス1.EventE発生時のいろいろ処理 40 41 _instance2.MethodM(); // ここでは_isntance2.EventEが発生してほしくない 42 } 43 44 private void Instance2_EventE(object obj, EventArgs e) 45 { 46 // インスタンス2.EventE発生時のいろいろ処理 47 48 _instance1.MethodM(); // ここでは_isntance1.EventEが発生してほしくない 49 } 50 51 public void MethodA() 52 { 53 // Hoge.MethodAとしていろいろ処理 54 55 _instance1.MethodM(); // ここでは_isntance1.EventEが発生してほしい 56 } 57 58 public void MethodB() 59 { 60 // Hoge.MethodBとしていろいろ処理 61 62 _instance2.MethodM(); // ここでは_isntance2.EventEが発生してほしい 63 } 64} 65

この状態のままMethodA,Bのどちらかを実行したら、当然相互にイベントが発生し続けてしまうわけで、それは困るということです。
現状はこのInstance1_EventE,Instance2_EventEの中でReaderWriterLock(実コードにてそういうロックが必要なのでそのまま流用しています。)のWriterでロックを掛けておいて、さらにその中でイベントハンドラの解除・追加を行うようにしていますが、それ以外に良い方法は無いかなぁと模索している状況です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

クラスAの作りって以下のような感じになってると考えていいんでしょうか?

public void MethodM()
{
// ここで色々な処理… 仮に作業Aとする

OnEventE(); // 別インスタンスのイベントに紐づいており、MethodM が呼び出される

}

であれば、メソッドMをリファクタリングし、「処理」と「イベント呼び出し」の二つに分けてはいかがでしょうか。

上でいう作業Aの部分をプライベート関数にし、OnEventの処理を切り離します。
private methodM();

MethodMの中ではmethodMとOnEvent関数を呼び出します。
public MethodM()
{
methodM();
OnEventE();
}

で、OnEventEではこのプライベートなmethodMの方を呼びます。
同じクラスの別インスタンスということなので、プライベートメソッドでも呼び出すことができます。
ちょっと違和感あるかもしれませんが…。

投稿2015/04/15 11:14

haru666

総合スコア1591

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ryzna

2015/04/16 02:12

ご提案ありがとう御座います。 > クラスAの作りって以下のような感じになってると考えていいんでしょうか? OnEventEは自インスタンスのイベントを発生させております。 今回新規に作ったコード(まったく別のクラス)上でたまたま片方のイベント発生時に別インスタンスのメソッドMを実行する必要が出てきた状態です。 > であれば、メソッドMをリファクタリングし、「処理」と「イベント呼び出し」の二つに分けてはいかがでしょうか。 確かにこの二つに分離して処理のみ、処理+イベント発生を組み合わせられるようにする構造としておけば解決できるわけですよね。 ただ、今回はClassA自身が別インスタンスを保持するわけではないのでご提案いただいているコードは実現できなさそうです。 やるとしたらイベントを発生させる代わりにデリゲートで処理を受け取る形間でしか出来無そうですが、それをやると既存コードへの影響が甚大なので流用ではなくコピペで別クラスにせざるを得ないです; 新たにイベントを持つクラスを設計する際にこの考え方を参考にさせていただきたいと思います。
guest

0

ベストアンサー

はじめのイベントを発生させるタイミングは捕まえることができるのでしょうか?
それならば私なら一旦イベントを抜いてしまうと思います。
インスタンス1のイベントEが発生するタイミングで


インスタンス2.イベントE -=(イベント)
インスタンス1.イベントE
インスタンス2.イベントE +=(イベント)

といった具合でインスタンス1のイベントが終了したタイミングで
イベントを戻します。
ご期待の処理にかなっているかわかりませんが参考までに。

投稿2015/04/15 07:44

torakichi

総合スコア14

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ryzna

2015/04/15 11:12

ご提案ありがとう御座います。 まったく同じ事を考えてとりあえず実装したのがこの方法です。 動きとしては今のところ問題もなく動いては居るのですが、「補足と現状での考え: 」にも書きましたようにこの方法で良いのかなぁ・・・もっといい方法ないかなぁと思い質問を投稿させていただきました。 もし他の方法をご存知でいらっしゃればご教示頂ければ幸いです。
guest

0

書いているうちにごちゃごちゃしてきて恥ずかしいことになってますが、伝わればいいなと思って投稿します。
イベントが既に発火しているか否かの状態を表す列挙体を定義し、それを内部変数に持つEventArgsの派生型を使うことで判断しようという試みです。

lang

1class Program 2{ 3 static void Main(string[] args) 4 { 5 var instance1 = new ClassA(); 6 var instance2 = new ClassA(); 7 8 instance1.EventE += (sender, e) => 9 { 10 if (e.Status == EventStatus.NotFired) 11 { 12 instance2.MethodM(EventStatus.Fired); 13 } 14 }; 15 instance2.EventE += (sender, e) => 16 { 17 if (e.Status == EventStatus.NotFired) 18 { 19 instance1.MethodM(EventStatus.Fired); 20 } 21 }; 22 23 instance1.MethodM(EventStatus.NotFired); 24 } 25} 26 27class ClassA 28{ 29 public event EventHandler<MyEventArgs> EventE; 30 protected virtual void OnEventE(MyEventArgs e) 31 { 32 if (this.EventE != null) 33 this.EventE(this, e); 34 } 35 public void MethodM(EventStatus status) 36 { 37 // ここに行いたい処理を入れる 38 39 if (status == EventStatus.NotFired) 40 this.OnEventE(new MyEventArgs(EventStatus.NotFired)); 41 } 42} 43 44class MyEventArgs : EventArgs 45{ 46 public EventStatus Status { get; private set; } 47 48 public MyEventArgs(EventStatus status) 49 { 50 this.Status = status; 51 } 52} 53 54enum EventStatus 55{ 56 NotFired, 57 Fired, 58}

投稿2015/04/14 13:20

htsign

総合スコア870

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ryzna

2015/04/15 01:37

ご提案ありがとう御座います。 なるほど。 イベントからの実行であるかメソッドパラメータで受け取り、そのままイベントパラメータに引き継いでハンドラ内で判断するということですね。 メソッドパラメータに既定値としてNotFiredを設定しておけば既存のコードに手を入れなくてすみますね。既存コードでイベントパラメータのステータスを気にする必要は無いはずですし。 「クラスA自体の修正」案として保留させていただきます。
guest

0

「イベントEからメソッドMが呼び出されても処理しない」のではなく
「イベントEからメソッドMを呼び出さない」という実装でも構わないですか?

自分であれば、

クラスAを継承したクラスA'を作成
クラスA'にイベントEを発生させないメソッドM'を作成(イベントEを発生させないだけであとはコピー)
インスタンス1,インスタンス2をクラスA'で生成するように修正
各インスタンスのイベントEのハンドラを各メソッドM'に変更

という感じに実装しますかね。。。

やはりリリース後ということですのでフラグで分岐を増やしたり
既存のコードに修正が入ったりする実装は避けたいですね。

投稿2015/04/14 06:54

wakuwaku

総合スコア386

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ryzna

2015/04/14 09:09

ご提案ありがとう御座います。 > 「イベントEからメソッドMを呼び出さない」という実装でも構わないですか? 条件としては問題ありませんが、 > クラスA'にイベントEを発生させないメソッドM'を作成(イベントEを発生させないだけであとはコピー) については修正やら改変やらといったコード管理の点で避けたい手法ですね。。。 >やはりリリース後ということですのでフラグで分岐を増やしたり >既存のコードに修正が入ったりする実装は避けたいですね。 まったく持ってその通りです・・・OTL
wakuwaku

2015/04/14 09:43

>> クラスA'にイベントEを発生させないメソッドM'を作成(イベントEを発生させないだけであとはコピー) >については修正やら改変やらといったコード管理の点で避けたい手法ですね。。。 クラスAのMのイベント以外の処理をリファクトしてイベントハンドラを イベント以外の処理にするのが妥当ですかね。 この修正でロジックが変わる訳ではないですし。 public class A{ public void M(){ this.M_nonEvent(); E(); // Eイベント } public M_nonEvent(){ // Mのイベント以外の処理 } } ただ私はクラスA'の後に、さらに修正の必要が発生したらクラスA''とか作ってそうですw
Ryzna

2015/04/14 10:28

なるほど。。。 修正範囲が拡大しそうな気がするのでご提案頂いた方法は見送らせていただこうと思います。 最初から今回のような用法を考慮したつくりに出来ればよかったんですがなかなかうまくいかないもんです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問