2016-08-03 96 views
2

我們目前正在開發一個日誌記錄解決方案,並實現了一個名爲「Log」的擴展方法。在寫入日誌文件時,我們理想的情況是編寫原始變量名稱(而不是擴展方法中使用的變量名稱)。從擴展方法中獲取原始變量名稱

我們的用戶目前該做的是:

public void DoSomeWork() 
{ 
    String testString = "Hey look I'm a string!"; 
    testString.Log("testString value"); 
} 

有了進一步擴展方法:

public static String Log(this String valueToStore, String name) 
{ 
    // The logging code which uses the 'name' parameter 
} 

這裏的問題是,它難以閱讀的代碼更長的線和看起來聚集。什麼是理想的是這樣的:

public void DoSomeWork() 
{ 
    String testString = "Hey look I'm a string!"; 
    testString.Log(); 
} 

隨着擴展方法:

public static String Log(this String valueToStore) 
{ 
    // The logging code which is able to retrieve the 
    // value 'testString' from the 'valueToStore' value 
} 

通過使用反射這在所有可能的?我知道nameof選項,但只在擴展方法中使用時才返回字符串'valueToStore'。

+0

我不認爲這是可能的。這些信息在執行時會丟失(一個變量甚至不是您可以反思的屬性或字段)。你可以遍歷堆棧框架並檢查它的字符串變量的調用方法,但這沒有幫助。順便說一句:你會期望從「hello world」.Log()'? –

+0

擴展方法是否不需要保持鏈接到被調用的地方?我知道這是不可能使用常規方法,但認爲擴展方法可能會提供一些額外的 –

+0

不,擴展方法只是語法糖。你的例子中的'testString.Log()'和'YourExtensionClass.Log(testString)'沒有區別。 –

回答

2

您可以使用Expression來實現這一點,但性能方面也未必是最好的選擇:

public static void Log<T>(Expression<Func<T>> expr) 
{ 
    var memberExpr = expr.Body as MemberExpression; 

    if (memberExpr == null) 
     return; 

    var varName = memberExpr.Member.Name; 
    var varData = expr.Compile()(); 

    // actual logging 
    ... 
} 

用法:

var test = "Foo"; 
Log(() => test); 

或者,如果您正在使用C#6.0 ,使用nameof運營商可以得到更好一點:

test.Log(nameof(test)); 

更好的解決方案是利用編譯器能力(特別是「Roslyn」編譯器)並在編譯時提供成員名稱。

+0

作爲擴展方法將如何實現這個用法呢,還是這個解決方案會從這個方法中解脫出來? –

+0

@ChrisWright,你可以將它擴展爲擴展方法,但它看起來很尷尬:'test.Log((()=> test)'。如果您願意,我可以修改答案以反映這一點。 – haim770

+0

它應該沒問題,我把它作爲一個標準方法粘貼,並打電話看到它的工作,所以我可以從那裏轉換它。感謝您的回答! –

0

不是一個真正的答案,更多的是一個指針,但是你可以嘗試用你正在使用的應用程序(例如visual studio)做一些事情,而不是在代碼中做。我的意思是使它重寫所有看起來像[variable].Log();[variable].Log([variable])

我很確定必須有一些奇怪的宏或插件在編譯前爲你做這個。

0

那麼,簡短的回答是否定的。變量名稱不保證在以不變形式編譯後保留。該信息必須以某種方式持續存在(例如,使用nameof())。此外,變量名稱可能不存在("test".GetVarName())。

長的答案是:是的,可能的,但它是最可笑的事情我已經在我的生活中創建一個:

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Reflection; 

namespace Test1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var myVarName = "test"; 
      myVarName.Test(); 
      Console.ReadKey(); 
     } 
    } 

    static class Extensions 
    { 
     public static void Test(this string str, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", 
     [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", 
     [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) 
     { 
      var relevantLine = File.ReadAllLines(sourceFilePath)[sourceLineNumber-1]; 
      var currMethodName = MethodInfo.GetCurrentMethod().Name; 
      var callIndex = relevantLine.IndexOf(currMethodName + "()"); 
      var sb = new Stack<char>(); 
      for (var i = callIndex - 2; i >= 0; --i) 
      { 
       if (char.IsLetterOrDigit(relevantLine[i])) 
        sb.Push(relevantLine[i]); 

      } 
      Console.WriteLine(new string(sb.ToArray())); 
     } 
    } 
}