0

この質問は、以前の質問How to get a IDictionary<string, object> of the parameters previous method called in C#? に関連しています。. コードを書きましたが、まだ欠けている部分があります。パラメータから値を取得するにはどうすればよいですか?

次のコードを実行すると、出力にはパラメーターの名前のみが表示され、値は表示されません。

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Question {
    internal class Program {
        public static void Main(string[] args) {
            var impl = new Implementation();
            var otherClass = new OtherClass { Name = "John", Age = 100 };
            impl.MethodA(1, "two", otherClass);
        }
    }

    internal class Implementation {
        public void MethodA(int param1, string param2, OtherClass param3) {
            Logger.LogParameters();
        }
    }

    internal class OtherClass {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    internal class Logger {
        public static void LogParameters() {
            var parameters = GetParametersFromPreviousMethodCall();
            foreach (var keyValuePair in parameters)
                Console.WriteLine(keyValuePair.Key + "=" + keyValuePair.Value);
        }

        private static IDictionary<string, object> GetParametersFromPreviousMethodCall() {
            var stackTrace = new StackTrace();
            var frame = stackTrace.GetFrame(2);
            var method = frame.GetMethod();

            var dictionary = new Dictionary<string, object>();
            foreach (var parameterInfo in method.GetParameters())
                dictionary.Add(parameterInfo.Name, parameterInfo.DefaultValue);
            return dictionary;
        }
    }
}
4

2 に答える 2

3

あなたのコードはおそらく行き止まりです。スタック フレームには、パラメーター値を取得できるものは何もありません。

ただし、このタスクを実行することは完全に可能です。あなたが望むことは、プロファイラーを書くことと非常に似ています。ログに記録するメソッドにコードを挿入して、そのパラメーターをベクトル化する必要があります。次のようなクラスから始めたとしましょう。

public class ParameterBlob {
    public ParameterInfo Info { get; set; }
    public object Value { get; set; }
}

そして、次のようなメソッドがあるとしましょう:

public static void LogMethodCall(MethodInfo method, param ParameterBlob[] parameterBlobs) { /* ... */ }

多かれ少なかれ注入したいものは次のとおりです。

MethodInfo methodInfo = MyLogging.GetMyMethodInfo();
ParameterBlob[] blobs = new ParameterBlobs[MyLogging.GetMyParameterCount(methodInfo);
ParameterBlob blob = new ParameterBlob();
blob.Info = MyLogging.GetParameterInfo(methodInfo, 0);
blob.Value = param0; // More on this
blobs[0] = blob;
blob = new ParameterBlob();
blob.Info = MyLogging.GetParameterInfo(methodInfo, 1);
blob.Value = param1; // More on this
blobs[1] = blob;
// ...
blob = new ParameterBlob();
blob.Info = MyLogging.GetParameterInfo(methodInfo, n);
blob.Value = paramn; // more on this
blobs[n] = blob;
MyLogging.LogMethodCall(methodInfo, blobs);

それで、「これについてもっと」と言う行は?実際にはそれらを書くことはできません。しかし、独自のパラメーターを参照してそれを行うサンプル ルーチンを作成することはできます。ldarg命令とstloc命令 (およびその間のいくつかの命令) があります。ポイントは、C# で記述し、コンパイラとILDASMを使用して 1 つのパラメーターに必要な正しいコードを表示し、そのCILを生成するルーチンを記述して、それを .NET にプラグインすることです。プロファイリング API を使用して、任意のルーチンにアタッチできます。

詳細については、記事「.NET Framework プロファイリング API を使用してオンザフライで MSIL コードを書き換える」を参照してください。

また、属性を使用して、メソッドをログに記録できるかどうかをマークすることもできます。

唯一の問題は、これを行うには実行時にアクセスする必要があることですが、そうでない場合もあります。あなたは完全に運が悪いですか?いいえ。

Cecilを使用すると、実行前にアセンブリ全体にアクセスし、前処理してロギング呼び出しを挿入できます。Cecil は信じられないほど簡単で、アセンブリを書き直してロギング呼び出しを含めるには数日かかるはずです。ターゲット アセンブリでこれを行っていることを先験的に知ることができ、適切な参照が既に設定されている場合は少なくなります。基本的に、アセンブリ内のすべてのメソッドにアクセスし、ログに記録できる場合は、上記のサンプルと同様に、CIL を挿入してすべてのパラメーターをログに記録します。

于 2012-04-09T20:50:59.040 に答える
2

以前の質問 (呼び出し元のメソッドのパラメーターの値を取得するにはどうすればよいですか? ) によると、StackTrace を介して取得することはできません。

AOPはあなたの友達です。

于 2012-04-09T20:38:54.140 に答える