2008-09-16 49 views
36

讓我用下面的例子來說明我的問題:找到傳遞給函數的變量名

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

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

當我傳遞變量「WhatIsMyName」的例子功能,我希望能夠得到一個原始變量名稱的字符串。也許是這樣的:

Variable.OriginalName.ToString() 

有沒有辦法做到這一點?

+2

嗯,你爲什麼會想這樣做?我只需要了解這個 – 2008-09-16 13:31:00

+0

背後的邏輯,我認爲有人想在Ruby中做同樣的事情。你可以搜索並看看他是否得到了有關如何去做的指示。 – Gishu 2008-09-16 13:31:08

+7

這是一個反覆出現的主題,推動了我的堅果。一個智力問題將被用來做一些事情,而不是一個答案..人們想知道你爲什麼想要做..有足夠的邏輯來回答這個問題...... – 2014-08-16 21:09:24

回答

-6

**否**我不這麼認爲。

您使用的變量名稱是爲了方便和易讀。編譯器不需要它&只是如果我沒有弄錯,就把它夾緊。

如果有幫助,您可以定義一個名爲NamedParameter並具有屬性Name和Param的新類。然後您將這個對象作爲參數傳遞。

49

你想要什麼是不可能的直接,但你可以使用表達式在C#3.0:

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

ExampleFunction(x => WhatIsMyName); 

請注意,這依賴於不確定的行爲,雖然它的工作在微軟目前的C#和VB編譯器,和在Mono的C#編譯器中使用了,但不能保證這在以後的版本中不會停止工作。

-1

簡短的回答是否定的,除非你真的很有動力。

要做到這一點的唯一方法是通過反射和堆棧行走。你必須得到一個堆棧框架,找出你調用的調用函數中的位置,然後使用CodeDOM嘗試找到樹的正確部分來查看錶達式的內容。

例如,如果調用是ExampleFunction(「a」+「b」)?

0

不是。對字符串變量的引用傳遞給函數 - 沒有任何關於它的固有metadeta。即使反射也不會讓你擺脫困境 - 從單一參考類型向後工作並不能讓你獲得足夠的信息來做你需要做的事情。

最好回到這個畫板上!

RP

0

你可以使用反射來獲取對象的所有屬性,除了通過它循環,並獲得其中名稱(財產)相匹配傳入的參數屬性的值。

-1

好看了一下。 當然你不能使用任何類型的信息。 此外,本地變量的名稱在運行時 不可用,因爲它們的名稱未編譯到程序集的元數據中。

4

不,但是每當你發現自己在做這種非常複雜的事情時,你可能想重新考慮你的解決方案。請記住,代碼應該比寫它更容易閱讀。

2

System.Environment.StackTrace將爲您提供一個包含當前調用堆棧的字符串。您可以解析它以獲取包含每個調用的變量名稱的信息。

0

感謝所有的回覆。我想我只需要和我現在正在做的事情一起去。

對於那些想知道爲什麼我問上述問題的人。我有以下功能:

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; 
} 

我發送錯誤消息的數組和一個CSS類,然後將其返回作爲對網頁的字符串。

每次我調用這個函數,我必須定義sType。例如:

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

正如你所看到的,我的變量被稱爲aErrors,我的css類被稱爲錯誤。我希望我的感冒可以根據我發送的變量名稱找出要使用的類。

再次感謝所有的回覆。

0

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); 
16
static void Main(string[] args) 
{ 
    Console.WriteLine("Name is '{0}'", GetName(new {args})); 
    Console.ReadLine(); 
} 

static string GetName<T>(T item) where T : class 
{ 
    var properties = typeof(T).GetProperties(); 
    Enforce.That(properties.Length == 1); 
    return properties[0].Name; 
} 

更多細節在this blog post

10

方式三:

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)使用反射,但較之其他雙向更快。 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是再緩慢。

1

試試這個工具類,

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

是的!這是可能的。我一直在尋找這個解決方案很長一段時間,終於想出瞭解決它的黑客(這有點討厭)。我不建議將它用作程序的一部分,我只認爲它在調試模式下工作。對我來說,這並不重要,因爲我只用它作爲我的控制檯級調試工具,所以我可以做的:

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; 
     } 
    } 
0

做這個

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

或使用此類

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"); 
    }   
} 

代碼測試,最優雅的,我可以拿出手

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

代碼命名。

30

我知道這是一個老問題,但在C#6.0中,他們引入了應該解決此問題的運算符的名稱。運算符的名稱解析了傳遞給它的變量的名稱。

用途爲你的情況應該是這樣的:

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(...)都會在編譯時進行評估以生成一個字符串。它的參數在運行時不被評估,並被認爲是無法訪問的代碼(但它不會發出「無法訪問的代碼」警告)。

的更多信息,可以發現here

舊版本Ç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; 
}