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

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

ただいまの
回答率

90.45%

  • C#

    9274questions

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

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

解決済

回答 6

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,578

cancat

score 239

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

前提・実現したいこと

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

発生している問題・エラーメッセージ

メソッド名が必要です。
とエラーになります。
どこでどのMethedに名前をつければよいのでしょう。

該当のソースコード

private void OperationCore(int command, string text) {
            if (command == 1) Operation1(text);
            else if (command == 2) Operation2(text);
            else if (command == 3) Operation3(text);
            //....(以下100個とか)
        }

        private void Operation1(string text) {

        }

        private void Operation2(string text) {

        }

        private void Operation3(string text) {

        }
        //....(以下100個とか)


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

private void OperationCoreNEW(int command, string text) {
            MemberInfo[] members = t.GetMembers(
                 BindingFlags.Public | BindingFlags.NonPublic |
                 BindingFlags.Instance | BindingFlags.Static |
                 BindingFlags.DeclaredOnly);
            string allmethods = "";
            foreach (MemberInfo m in members) {
                if (m.MemberType == MemberTypes.Method) {
                    MethodInfo method = (MethodInfo)m;
                    if (method.Name == "Operation" + command)
                        method(text);//メソッド名が必要です。
                }
            }
        }

補足情報(言語/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 

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

+2

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/06 13:21

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

    キャンセル

  • 2016/10/07 11:46

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

    キャンセル

checkベストアンサー

+1

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

private Dictionary<string, MethodInfo> cache = new Dictionary<string, MethodInfo>();

private void OperationCoreNEW(int command, string text)
{
  MethodInfo mi = null;
  string methodName = "Operation" + command.ToString();

  if (!cache.TryGetValue(methodName, out mi))
  {
    mi = t.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
    cache.Add(methodName, mi);
  }
  mi?.Invoke(this, new object[] { text });
}


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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/07 11:35

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

    キャンセル

  • 2016/10/07 11:57

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

    キャンセル

  • 2016/10/07 13:45

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

    キャンセル

  • 2016/10/07 16:39

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

    キャンセル

  • 2016/10/07 17:26

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

    キャンセル

+1

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

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/07 11:38

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

    キャンセル

+1

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

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


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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/07 11:31

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

    キャンセル

  • 2016/10/07 11:39

    私の書いた書き方は、メソッドは一回メンテ箇所も一か所なので、ミスが発生する要素はないと思うのですが、ほかに書いていない条件はありますか?

    あと、リフレクションを使うなら、属性(メソッド宣言の上に[]で書く)をつける方法もありますね。

    キャンセル

  • 2016/10/07 11:49

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

    キャンセル

  • 2016/10/07 11:51

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

    キャンセル

  • 2016/10/07 13:45 編集

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

    キャンセル

  • 2016/10/07 15:08

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

    キャンセル

  • 2016/10/07 16:08 編集

    Foo()のみをメンテナンスすればよいという方法です。Foo()をメンテナンスした後でOperationCoreをメンテする必要はありません。

    commandList.Add(n, string s => {...});//nはコマンドの番号
    この中の...にこれまでのCommandNの処理を書くのです。(説明が足りてませんでした。)

    キャンセル

  • 2016/10/07 16:14 編集

    Fooの部分はそうですが、関数の中身はさすがに省略のしようがないです。
    それは他のどのやり方でも同じです。(ベストアンサーのやり方でも)

    commandList.Add(5, (string s => {return "result" + s;});
    関数の実装と登録が一体化しているので、漏れが生まれる可能性は有りえません。
    なによりリフレクションはこれの数百倍重いという事実を認識しておかないといけません。
    http://devlights.hatenablog.com/entry/20110216/p1

    キャンセル

  • 2016/10/07 16:18

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

    キャンセル

  • 2016/10/07 17:25

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

    キャンセル

  • 2016/10/07 21:20

    それは想像を超えた状況でした。

    それではこの方法ではめんどうかもしれませんね。

    キャンセル

  • 2016/10/09 18:39

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

    キャンセル

+1

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

public class OperationClass
{
    private readonly Dictionary<int, Action<string>> _operations; // Operationのキャッシュ

    public OperationClass() // コンストラクタ
    {
        this._operations = typeof(OperationClass)
            .GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)
            .Where(method => method.Name.StartsWith("Operation")) // 頭に"Operation"がつくメソッドを片っ端から収集
            .ToDictionary(
                method => int.Parse(method.Name.Substring("Operation".Length)), // "Operation"の後ろには数字だけがつく前提
                method => (Action<string>)method.CreateDelegate(typeof(Action<string>), this));

        // 以下普通のコンストラクタ処理
        // ...
    }

    public void Run(int command, string text) // OperationCoreのリネーム
    {
        this._operations[command].Invoke(text);
    }

    private void Operation1(string text)
    {
        // ...
    }

    private void Operation2(string text)
    {
        // ...
    }

    // ...
}

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/07 19:02

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

    キャンセル

0

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

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/07 11:42

    情報ありがとうございます。
    テストではなく、本番のコードです。

    おっしゃるとおり、いま開発中で、工数削減とミス防止のために、検討しています。
    実際にMethodを作ったがOperationCoreから呼び出しを忘れるミスが発生していて、ミス防止のため、Methodを作ったら自動で呼べるようにしたいわけです。

    キャンセル

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

  • ただいまの回答率 90.45%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    9274questions

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