我正在編寫一些調試/測試代碼,其中我想要顯示原始表達式並對任何表達式進行評估。
對於(瑣碎的)例子:
IList<string> myString = /* Some string collection expression */
ShowMe(myString.Select(s => s.ToLower()));
我實現ShowMe
如下:
public void ShowMe(/* Not sure what has to go here */)
{
/* Not sure what has to go here */
Console.WriteLine(expression.ToString();
IEnumerable result = expression.Evaluate(); // or something
foreach(item in result)
{
Console.WriteLine(/* etc. */)
}
}
結果將寫入控制台:
myString.Select(s => s.ToLower())
(第一項)
(下一個項目
(等等...)
換句話說,我的ShowMe
方法在表達式樹上運行而不是表達式的值,因此它既可以顯示給定的表達式,也可以顯示計算結果。
我不能簡單地將ShowMe
聲明為:
public void ShowMe(Expression expr)
......但如果我宣佈為
public void ShowMe(Expression<Func<Enumerable>> expr)
...它有點工作 - 我必須用lambda表達式調用我的方法:
ShowMe(() => myString.Select(s => s.ToLower()))
......我不想做。
我有理由相信這可以做到...... FluentAssertions就是這麼做的。例如:如果我執行以下測試代碼行:
(1 + 1).Should.Be(3)
我得到以下結果:
預計(1 + 1)為3,但找到2。
FluentAssertion都評估了表達式(1 + 1)並捕獲了表達式樹,以便能夠顯示它所評估的原始表達式。
我看不出這是怎麼做的,但我想做類似的事情。我該怎麼做?
任何方法本身都無法做到這一點。
所有這些庫只解析堆棧跟踪並提取文件名和行號。然後從給定行的源代碼文件中提取表達式(這包括一些解析/驗證)。
值得注意的是,如果源代碼不存在/不可用,則表達式無法顯示。
找出可接受的折衷方案:
public static class ObjectHelper
{
public static void ToConsole<T>(this IEnumerable<T> enumerable, Expression<Func<T,object>> expr)
where T:class
{
var fn = expr.Compile();
var result = enumerable.Select(s => fn(s));
Console.WriteLine($"My data selected as {PrettyPrintExpression(expr)}");
foreach(var element in result)
{
Console.WriteLine(/* etc. */);
}
}
private static string PrettyPrintExpression(Expression<Func<T,object>> expr)
{
// Walk the expression tree to print as desired
}
}
...我可以調用:
IList<MyObject> list = /* etc. */
list.ToConsole(s => new{/* any members I want out of MyObject */});