web-dev-qa-db-ja.com

関数に渡された変数名を見つける

次の例を使用して私の質問を説明します。

public string ExampleFunction(string Variable) {
    return something;
}

string WhatIsMyName = "Hello World"';
string Hello = ExampleFunction(WhatIsMyName);

変数「WhatIsMyName」をサンプル関数に渡すと、元の変数名の文字列を取得できるようになります。おそらく次のようなもの:

Variable.OriginalName.ToString()

これを行う方法はありますか?

56
GateKiller

あなたが望むものは直接可能ではありませんが、C#3.0で式を使用できます:

public void ExampleFunction(Expression<Func<string, string>> f) {
    Console.WriteLine((f.Body as MemberExpression).Member.Name);
}

ExampleFunction(x => WhatIsMyName);

これは不特定の動作に依存しており、Microsoftの現在のC#およびVBコンパイラ、MonoのC#コンパイラではandで動作しますが、これが動作しないという保証はありません将来のバージョンで動作を停止します。

57
Konrad Rudolph

これは正確には不可能であり、望みの方法です。 C#6.0では、コードの改善と簡素化に役立つnameof演算子が導入されています。演算子の名前は、渡された変数の名前を解決します。

ケースの使用法は次のようになります。

public string ExampleFunction(string variableName) {
      //Construct your log statement using c# 6.0 string interpolation
       return $"Error occurred in {variableName}";
}

string WhatIsMyName = "Hello World"';
string Hello = ExampleFunction(nameof(WhatIsMyName));

主な利点は、コンパイル時に行われることです。

Nameof式は定数です。いずれの場合も、nameof(...)はコンパイル時に評価され、文字列が生成されます。その引数は実行時に評価されず、到達不能コードと見なされます(ただし、「到達不能コード」警告を発しません)。

詳細については、こちらをご覧ください こちら

C 3.0以上の旧バージョン
Nawfalsで構築するには

GetParameterName2(new { variable });

//Hack to assure compiler warning is generated specifying this method calling conventions
[Obsolete("Note you must use a single parametered AnonymousType When Calling this method")]
public static string GetParameterName<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    return typeof(T).GetProperties()[0].Name;
}
31
johnny 5

3つの方法:

1)まったく反射のないもの:

GetParameterName1(new { variable });

public static string GetParameterName1<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    return item.ToString().TrimStart('{').TrimEnd('}').Split('=')[0].Trim();
}

2)反射を使用しますが、これは他の2つよりもはるかに高速です。

GetParameterName2(new { variable });

public static string GetParameterName2<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    return typeof(T).GetProperties()[0].Name;
}

3)最も遅いので、使用しないでください。

GetParameterName3(() => variable);

public static string GetParameterName3<T>(Expression<Func<T>> expr)
{
    if (expr == null)
        return string.Empty;

    return ((MemberExpression)expr.Body).Member.Name;
}

コンボパラメータの名前と値を取得するために、これらのメソッドを拡張できます。もちろん、パラメータを別の引数として個別に渡すと値を取得するのは簡単ですが、それは洗練されていません。代わりに:

1)

public static string GetParameterInfo1<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    var param = item.ToString().TrimStart('{').TrimEnd('}').Split('=');
    return "Parameter: '" + param[0].Trim() +
           "' = " + param[1].Trim();
}

2)

public static string GetParameterInfo2<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    var param = typeof(T).GetProperties()[0];
    return "Parameter: '" + param.Name +
           "' = " + param.GetValue(item, null);
}

3)

public static string GetParameterInfo3<T>(Expression<Func<T>> expr)
{
    if (expr == null)
        return string.Empty;

    var param = (MemberExpression)expr.Body;
    return "Parameter: '" + param.Member.Name +
           "' = " + ((FieldInfo)param.Member).GetValue(((ConstantExpression)param.Expression).Value);
}

現在、1と2は同等の速度であり、3は再び低迷しています。

13
nawfal

いいえ。ただし、このように非常に複雑なことをしていることに気付いたときは、ソリューションを再考する必要があります。コードは、書くよりも読みやすいはずです。

4
Nate Kohari

はい!可能です。私はこれに対する解決策を長い間探していましたが、最終的にそれを解決するハックを思いつきました(少し厄介です)。これをプログラムの一部として使用することはお勧めしません。デバッグモードでのみ機能すると思います。私にとっては、コンソールクラスのデバッグツールとしてのみ使用するため、これは重要ではありません。

int testVar = 1;
bool testBoolVar = True;
myConsole.Writeline(testVar);
myConsole.Writeline(testBoolVar);

コンソールへの出力は次のようになります。

testVar: 1
testBoolVar: True

これを行うために使用する関数を次に示します(コンソールクラスのラッピングコードは含まれません)。

    public Dictionary<string, string> nameOfAlreadyAcessed = new Dictionary<string, string>();
    public string nameOf(object obj, int level = 1)
    {
        StackFrame stackFrame = new StackTrace(true).GetFrame(level);
        string fileName = stackFrame.GetFileName();
        int lineNumber = stackFrame.GetFileLineNumber();
        string uniqueId = fileName + lineNumber;
        if (nameOfAlreadyAcessed.ContainsKey(uniqueId))
            return nameOfAlreadyAcessed[uniqueId];
        else
        {
            System.IO.StreamReader file = new System.IO.StreamReader(fileName);
            for (int i = 0; i < lineNumber - 1; i++)
                file.ReadLine();
            string varName = file.ReadLine().Split(new char[] { '(', ')' })[1];
            nameOfAlreadyAcessed.Add(uniqueId, varName);
            return varName;
        }
    }
4
blooop

System.Environment.StackTraceは、現在の呼び出しスタックを含む文字列を提供します。それを解析して、各呼び出しの変数名を含む情報を取得できます。

2
kevin42

このユーティリティクラスを試してみてください。

public static class Utility
{
    public static Tuple<string, TSource> GetNameAndValue<TSource>(Expression<Func<TSource>> sourceExpression)
    {
        Tuple<String, TSource> result = null;
        Type type = typeof (TSource);
        Func<MemberExpression, Tuple<String, TSource>> process = delegate(MemberExpression memberExpression)
                                                                    {
                                                                        ConstantExpression constantExpression = (ConstantExpression)memberExpression.Expression;
                                                                        var name = memberExpression.Member.Name;
                                                                        var value = ((FieldInfo)memberExpression.Member).GetValue(constantExpression.Value);
                                                                        return new Tuple<string, TSource>(name, (TSource) value);
                                                                    };

        Expression exception = sourceExpression.Body;
        if (exception is MemberExpression)
        {
            result = process((MemberExpression)sourceExpression.Body);
        }
        else if (exception is UnaryExpression)
        {
            UnaryExpression unaryExpression = (UnaryExpression)sourceExpression.Body;
            result = process((MemberExpression)unaryExpression.Operand);
        }
        else
        {
            throw new Exception("Expression type unknown.");
        }

        return result;
    }


}

好きなユーザー

    /*ToDo : Test Result*/
    static void Main(string[] args)
    {
        /*Test : primivit types*/
        long maxNumber = 123123;
        Tuple<string, long> longVariable = Utility.GetNameAndValue(() => maxNumber);
        string longVariableName = longVariable.Item1;
        long longVariableValue = longVariable.Item2;

        /*Test : user define types*/
        Person aPerson = new Person() { Id = "123", Name = "Roy" };
        Tuple<string, Person> personVariable = Utility.GetNameAndValue(() => aPerson);
        string personVariableName = personVariable.Item1;
        Person personVariableValue = personVariable.Item2;

        /*Test : anonymous types*/
        var ann = new { Id = "123", Name = "Roy" };
        var annVariable = Utility.GetNameAndValue(() => ann);
        string annVariableName = annVariable.Item1;
        var annVariableValue = annVariable.Item2;

        /*Test : Enum tyoes*/
        Active isActive = Active.Yes;
        Tuple<string, Active> isActiveVariable = Utility.GetNameAndValue(() => isActive);
        string isActiveVariableName = isActiveVariable.Item1;
        Active isActiveVariableValue = isActiveVariable.Item2;
    }
2
Dipon Roy

これを行う

var myVariable = 123;
myVariable.Named(() => myVariable);
var name = myVariable.Name();
// use name how you like

またはコードを手動で命名する

var myVariable = 123.Named("my variable");
var name = myVariable.Name();

このクラスを使用して

public static class ObjectInstanceExtensions
{
    private static Dictionary<object, string> namedInstances = new Dictionary<object, string>();

    public static void Named<T>(this T instance, Expression<Func<T>> expressionContainingOnlyYourInstance)
    {
        var name = ((MemberExpression)expressionContainingOnlyYourInstance.Body).Member.Name;
        instance.Named(name);            
    }

    public static T Named<T>(this T instance, string named)
    {
        if (namedInstances.ContainsKey(instance)) namedInstances[instance] = named;
        else namedInstances.Add(instance, named);
        return instance;
    }        

    public static string Name<T>(this T instance)
    {
        if (namedInstances.ContainsKey(instance)) return namedInstances[instance];
        throw new NotImplementedException("object has not been named");
    }        
}

コードはテスト済みで、最もエレガントです。

1
kernowcode

GateKiller、私の回避策の何が問題になっていますか?あなたはそれを使用するためにあなたの関数を簡単に書き換えることができます(私はその場で関数を改善するために自由を取りました):

static string sMessages(Expression<Func<List<string>>> aMessages) {
    var messages = aMessages.Compile()();

    if (messages.Count == 0) {
        return "";
    }

    StringBuilder ret = new StringBuilder();
    string sType = ((MemberExpression)aMessages.Body).Member.Name;

    ret.AppendFormat("<p class=\"{0}\">", sType);
    foreach (string msg in messages) {
        ret.Append(msg);
        ret.Append("<br />");
    }
    ret.Append("</p>");
    return ret.ToString();
}

次のように呼び出します。

var errors = new List<string>() { "Hi", "foo" };
var ret = sMessages(() => errors);
0
Konrad Rudolph

すべての回答をありがとう。私は今やっていることと一緒に行かなければならないと思います。

上記の質問をした理由を知りたい人のために。次の機能があります。

string sMessages(ArrayList aMessages, String sType) {
    string sReturn = String.Empty;
    if (aMessages.Count > 0) {
        sReturn += "<p class=\"" + sType + "\">";
        for (int i = 0; i < aMessages.Count; i++) {
            sReturn += aMessages[i] + "<br />";
        }
        sReturn += "</p>";
    }
    return sReturn;
}

エラーメッセージの配列と、Webページの文字列として返されるcssクラスを送信します。

この関数を呼び出すたびに、sTypeを定義する必要があります。何かのようなもの:

output += sMessages(aErrors, "errors");

ご覧のとおり、変数はaErrorsと呼ばれ、cssクラスはerrorsと呼ばれます。私は、送信した変数名に基づいて、使用するクラスが風邪でわかることを望んでいました。

繰り返しますが、すべての回答に感謝します。

0
GateKiller

いいえ。文字列変数への参照は関数に渡されます。それに関する固有のメタデータは含まれていません。リフレクションを使用しても、ここから森から抜け出すことはできません。単一の参照タイプから逆方向に作業しても、必要なことを行うのに十分な情報が得られません。

このボードの図面に戻りましょう!

rp

0
rp.

リフレクションを使用して、オブジェクトのすべてのプロパティを取得し、ループすることより、(プロパティの)名前が渡されたパラメータと一致するプロパティの値を取得できます。

0
Adam Vigh