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

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

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

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

Q&A

解決済

6回答

5361閲覧

C#で複数のmethodを1回で呼びたいです。

cancat

総合スコア313

C#

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

0グッド

0クリップ

投稿2016/10/05 09:36

編集2016/10/05 09:37

こんにちは。
Windows10でC#のアプリケーションを開発しています。
Visual Studio 2015 Communityを使っています。

###前提・実現したいこと
複数のmethodを1回で呼びたいです。

###発生している問題・エラーメッセージ
メソッド名が必要です。
とエラーになります。
どこでどのMethedに名前をつければよいのでしょう。

###該当のソースコード

C#

1 private void OperationCore(int command, string text) { 2 if (command == 1) Operation1(text); 3 else if (command == 2) Operation2(text); 4 else if (command == 3) Operation3(text); 5 //....(以下100個とか) 6 } 7 8 private void Operation1(string text) { 9 10 } 11 12 private void Operation2(string text) { 13 14 } 15 16 private void Operation3(string text) { 17 18 } 19 //....(以下100個とか)

これを簡単にしたいです。

C#

1 private void OperationCoreNEW(int command, string text) { 2 MemberInfo[] members = t.GetMembers( 3 BindingFlags.Public | BindingFlags.NonPublic | 4 BindingFlags.Instance | BindingFlags.Static | 5 BindingFlags.DeclaredOnly); 6 string allmethods = ""; 7 foreach (MemberInfo m in members) { 8 if (m.MemberType == MemberTypes.Method) { 9 MethodInfo method = (MethodInfo)m; 10 if (method.Name == "Operation" + command) 11 method(text);//メソッド名が必要です。 12 } 13 } 14 } 15

###補足情報(言語/FW/ツール等のバージョンなど)
Microsoft Visual Studio Community 2015
Version 14.0.25424.00 Update 3
Microsoft .NET Framework
Version 4.6.01038

インストールしているバージョン:Community

Visual C# 2015 00322-20000-00000-AA575
Microsoft Visual C# 2015

です。
よろしくお願いします。

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

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

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

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

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

guest

回答6

0

他の方が代表的なやり方を一通り紹介しているので変わり種を一つ。
T4Templateという機能を使ってコードそのものを自動生成するやり方です。
このやり方は実行時に余計な事はしないので、他のやり方より実行処理速度は速いです。
T4Templateを使って状態遷移表からStateパターンを自動生成する
T4Templateのリファレンス

投稿2016/10/06 03:28

ishi9

総合スコア1294

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

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

KSwordOfHaste

2016/10/06 04:21

なるほどと思い試してみました。作成しようとしているアセンブリーのNameSpaceはテンプレート生成コードから利用できなさそう(鶏が先か卵が先か的問題で?)に見えたので質問者さんのやろうとしてることを直接実現できるかは少々疑問は残りましたが外部に定義されたファイルなどからソースを展開したい場合には使いでがありそうですね!
cancat

2016/10/07 02:46

おおお。これは悪くなさそうです。 引数のstring textは実際にはjsonなので、jsonからcodeを自動生成できれば、自動化できそう。 こういうケースは多いので、とても参考になりました。ありがとうございます。
guest

0

こんにちは。
既に解決済みになってますが、ひとつアイデアを思いついたので回答します。

csharp

1 2public class OperationClass 3{ 4 private readonly Dictionary<int, Action<string>> _operations; // Operationのキャッシュ 5 6 public OperationClass() // コンストラクタ 7 { 8 this._operations = typeof(OperationClass) 9 .GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) 10 .Where(method => method.Name.StartsWith("Operation")) // 頭に"Operation"がつくメソッドを片っ端から収集 11 .ToDictionary( 12 method => int.Parse(method.Name.Substring("Operation".Length)), // "Operation"の後ろには数字だけがつく前提 13 method => (Action<string>)method.CreateDelegate(typeof(Action<string>), this)); 14 15 // 以下普通のコンストラクタ処理 16 // ... 17 } 18 19 public void Run(int command, string text) // OperationCoreのリネーム 20 { 21 this._operations[command].Invoke(text); 22 } 23 24 private void Operation1(string text) 25 { 26 // ... 27 } 28 29 private void Operation2(string text) 30 { 31 // ... 32 } 33 34 // ... 35}

リフレクションは重い処理なので、「クラスのインスタンス生成時に総なめして一気にメソッドキャッシュを作ってしまう」というやり方です。各Operationの実行時には通常のメソッド呼び出しと変わらない速度になります。
まあ、その分インスタンス生成時が特別に重いんですが。最初だけ重いけど後が軽ければいいという割り切りですね。できるならリフレクションは使わないに越したことはないです。
あと、現状のコードでは、「頭に"Operation"がつく、パブリックでないインスタンスメソッド」を片っ端からDictionaryに放り込むので、OperationCoreみたいな名前のメソッドが存在すると例外を吐いて死にます。名前を変えれば問題ないですが。

投稿2016/10/07 09:10

編集2016/10/07 09:46
tamoto

総合スコア4103

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

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

cancat

2016/10/07 10:02

ありがとうございます。おっしゃるとおり、どこで実行するかのタイミングは重要ですね。
guest

0

リフレクションは面倒なので私ならこう書きます。

C#

1public class Foo{ 2 private Dictionary<int, Action<string>> commandList; 3 public Foo(){//事前に実行されるメソッドならばなんでも良い 4 commandList = new Dictionary<int, Action<string>> (); 5 commandList.Add(1, (string s => {...}); 6 commandList.Add(2, (string s => {...}); 7 commandList.Add(3, (string s => {...}); 8 commandList.Add(4, (string s => {...}); 9 ... 10 } 11 private void OperationCore(int command, string text) { 12 commandList[command](text); 13 } 14

無名関数を作るところで少し重いかもしれませんが、100個程度なら問題ないと思います。(多分リフレクションより軽い)

投稿2016/10/05 13:50

iwamoto_takaaki

総合スコア2883

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

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

cancat

2016/10/07 02:31

ありがとうございます。これも考えたのですが、手作業が多くてミスが発生しています。 なのでないかなー、という感じで相談しました。
iwamoto_takaaki

2016/10/07 02:39

私の書いた書き方は、メソッドは一回メンテ箇所も一か所なので、ミスが発生する要素はないと思うのですが、ほかに書いていない条件はありますか? あと、リフレクションを使うなら、属性(メソッド宣言の上に[]で書く)をつける方法もありますね。
cancat

2016/10/07 02:49

メソッド名を書くところで1回、Coreでそれを呼ぶので1回、合計2回です。 実際コーディングしていたら、Coreで呼ぶところで抜けミスしています。
cancat

2016/10/07 02:51

いま7つくらいですが、7*2=14でミスしています。 CoreをRefrectionで処理できれば、methodを作るだけなので、作業が半減し、少なくともCoreから呼んでいない、というミスは無くせるのではないかなと期待しています。
ishi9

2016/10/07 04:47 編集

OperationCoreは関数が増えても一切変更が入らない(メンテの必要が無い)と思いますが、どういったものを想定されているのでしょうか? ちなみに私は自分がやるならこの方法を選びます。(前提によりますが)
cancat

2016/10/07 06:08

このFoo()はメンテナンス必要ですよね?
iwamoto_takaaki

2016/10/07 07:08 編集

Foo()のみをメンテナンスすればよいという方法です。Foo()をメンテナンスした後でOperationCoreをメンテする必要はありません。 commandList.Add(n, string s => {...});//nはコマンドの番号 この中の...にこれまでのCommandNの処理を書くのです。(説明が足りてませんでした。)
ishi9

2016/10/07 07:16 編集

Fooの部分はそうですが、関数の中身はさすがに省略のしようがないです。 それは他のどのやり方でも同じです。(ベストアンサーのやり方でも) commandList.Add(5, (string s => {return "result" + s;}); 関数の実装と登録が一体化しているので、漏れが生まれる可能性は有りえません。 なによりリフレクションはこれの数百倍重いという事実を認識しておかないといけません。 http://devlights.hatenablog.com/entry/20110216/p1
iwamoto_takaaki

2016/10/07 07:18

ishi9さん、 わかりやすい説明ありがとうございます。コードで語るのもほどほどにしとかないといけませんね。参考になります。
cancat

2016/10/07 08:25

なるほど。 coreから呼んでいるOreration1, 2, 3, ...100は、それなりにコード量があるので、CommandLinst.Add(n, string s=> {...});の...にかける量を超えています。 現在は、Operation1.csのように、Operationごとにファイルを分けています。 そのくらいの分量です。 {...}にすべて書くとしたら、ざっくり30行としても30*7(現在)=210行、将来的に100になると3,000行にもなります。 とすると、この方法をとることはできない、という判断です。
iwamoto_takaaki

2016/10/07 12:20

それは想像を超えた状況でした。 それではこの方法ではめんどうかもしれませんね。
cancat

2016/10/09 09:39

ですねー。 このほうがsimpleなこともあるので、case by caseかなと思いました。 とても参考になりました。ありがとうございます。
guest

0

MethodInfoの利用方法が間違っています。

メソッドコールをする際は、
MethodInfoクラスのInvokeメソッドにクラスのインスタンスとパラメタを引き渡して実行します。

ただそもそもリフレクションを乱用し過ぎるのはよくないのですし、
推測となりますがメソッド数の多さからも、
クラス分割による責務の分離や、インターフェースや抽象クラス利用し、
多数の機能が一点集中するのを避ける設計にした方が良いでしょう。

メソッドコールの制御部と、
メソッドの実装部についても抽象クラスやインターフェースを導入すると実現できたりします。

投稿2016/10/05 10:20

編集2016/10/05 10:32
Panzer_vor

総合スコア1636

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

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

cancat

2016/10/07 02:38

ご助言ありがとうございます。 インターフェースや抽象クラスについて、時間のある時に調べてみます。
guest

0

ベストアンサー

MethodInfoクラスはデリゲートの代わりではありません。

c#

1private Dictionary<string, MethodInfo> cache = new Dictionary<string, MethodInfo>(); 2 3private void OperationCoreNEW(int command, string text) 4{ 5 MethodInfo mi = null; 6 string methodName = "Operation" + command.ToString(); 7 8 if (!cache.TryGetValue(methodName, out mi)) 9 { 10 mi = t.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic); 11 cache.Add(methodName, mi); 12 } 13 mi?.Invoke(this, new object[] { text }); 14}

一般的にリフレクションは重い処理と言われているので私はキャッシュを設けることが多いです。
オーバーロードがあり複数返るようなメソッド名を指定すると、この実装ではエラーになります。

投稿2016/10/05 10:12

編集2016/10/05 10:14
htsign

総合スコア870

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

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

cancat

2016/10/07 02:35

ありがとうございます。 これはNiceです。 mi = t.GetMethodのtはなにでしょう?
cancat

2016/10/07 02:57

sampleclass sc = new sampleclass(); Type t = sc.GetType(); でした。
cancat

2016/10/07 04:45

できました。とてもスマートです。感謝します。
htsign

2016/10/07 07:39

返事遅れました。すみません。 お察しの通り、t は Type です。 cancatさんのコードの変数名をそのまま取ってきた感じで書いたつもりでした。
cancat

2016/10/07 08:26

はい、ちょっとブランクあって寝ぼけてました。失礼しました。
guest

0

質問者さんのコードのBingindFlagsをみて「このクラスに定義したOperationXという名前の全部のメソッドをテストするために使うコードなのかな?」と感じました。基底クラスからの継承メソッドを対象外にしていてその他のものはインスタンスメソッドであろうがstaticメソッドであろうが何でも呼び出すというふうになっていますので。もしそういう目的で実行時のInvokeのオーバーヘッドを気にしないというなら単純に以下のようでいいと思います。

C#

1private void OperationCore(int command, string text) { 2 Method m = GetType().GetMethod("Operation" + command, 3 BindingFlags.Public | BindingFlags.NonPublic | 4 BindingFlags.Instance | BindingFlags.Static | 5 BindingFlags.DeclaredOnly); 6 if (m == null) 7 throw new ArgumentException("command out of range"); 8 m.Invoke(this, new object[] { s }); 9}

commandによる動的な処理の切り替えが必要でかつこれが本番のコードだとしたら、OperationXというメソッドが頻繁に追加されるような開発途中の段階でOperationCoreをメンテする工数を省く目的でこういう実装をすることはあるかも知れません。GetMethodが順サーチしていて(調べたことがないのですがそうなのでしょうか?)そこがボトルネックになるようならさらにhisignさんがおっしゃる方法やさらにDictionaryの代わりに配列にするなどを検討すべきと思います。

それ以上の性能的配慮もしくは設計方式がこれでよいかどうかについては質問内容の範囲を超えてしまうので、もし課題があるなら別トピックを起こすのがよろしいかと思います。

投稿2016/10/06 02:30

編集2016/10/06 03:33
KSwordOfHaste

総合スコア18392

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

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

cancat

2016/10/07 02:42

情報ありがとうございます。 テストではなく、本番のコードです。 おっしゃるとおり、いま開発中で、工数削減とミス防止のために、検討しています。 実際にMethodを作ったがOperationCoreから呼び出しを忘れるミスが発生していて、ミス防止のため、Methodを作ったら自動で呼べるようにしたいわけです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問