表達式樹練習實踐:C 迴圈 [TOC] C 提供了以下幾種迴圈類型。 | 迴圈類型 | 描述 | | : | : | | while 迴圈 | 當給定條件為真時,重覆語句或語句組。它會在執行迴圈主體之前測試條件。 | | for/foreach 迴圈 | 多次執行一個語句序列,簡化管理迴圈變數的代碼 ...
目錄
表達式樹練習實踐:C# 迴圈
C# 提供了以下幾種迴圈類型。
迴圈類型 | 描述 |
---|---|
while 迴圈 | 當給定條件為真時,重覆語句或語句組。它會在執行迴圈主體之前測試條件。 |
for/foreach 迴圈 | 多次執行一個語句序列,簡化管理迴圈變數的代碼。 |
do...while 迴圈 | 除了它是在迴圈主體結尾測試條件外,其他與 while 語句類似。 |
嵌套迴圈 | 您可以在 while、for 或 do..while 迴圈內使用一個或多個迴圈。 |
當然,還有以下用於控制迴圈的語句
控制語句 | 描述 |
---|---|
break 語句 | 終止 loop 或 switch 語句,程式流將繼續執行緊接著 loop 或 switch 的下一條語句。 |
continue 語句 | 引起迴圈跳過主體的剩餘部分,立即重新開始測試條件。 |
LabelTarget
LabelTarget 是用於創建迴圈標記的。
無論是 for 還是 while ,平時編寫迴圈時,都需要有跳出迴圈的判斷,有時需要某個參數自增自減並且作為判斷依據。
C# 表達式樹裡面是沒有專門表示 for /while 的,裡面只有一個 Loop。看一下Loop 生成的表達式樹
.Lambda #Lambda1<System.Func`1[System.Int32]>() {
.Block(System.Int32 $x) {
$x = 0;
.Loop {
.If ($x < 10) {
$x++
} .Else {
.Break #Label1 { $x }
}
}
.LabelTarget #Label1:
}
}
要實現迴圈控制,有 break,contauine 兩種 Expression:
public static GotoExpression Break(LabelTarget target, Type type);
public static GotoExpression Break(LabelTarget target, Expression value);
public static GotoExpression Break(LabelTarget target);
public static GotoExpression Break(LabelTarget target, Expression value, Type type);
public static GotoExpression Continue(LabelTarget target, Type type);
public static GotoExpression Continue(LabelTarget target);
所以,要實現迴圈控制,必須要使用 LabelTarget,不然就無限迴圈了。
要理解 LabelTarget ,最好的方法是動手做。
for / while 迴圈
Expression.Loop 用於創建迴圈,包括 for 和 while,定義如下
public static LoopExpression Loop(Expression body, LabelTarget @break, LabelTarget @continue);
System.Linq.Expressions.LoopExpression.
public static LoopExpression Loop(Expression body);
public static LoopExpression Loop(Expression body, LabelTarget @break);
表達式樹裡面的迴圈,只有 Loop,無 for / while 的區別。
那麼,我們來一步步理解 Loop 迴圈和 LabelTarget;
無限迴圈
while (true)
{
Console.WriteLine("無限迴圈");
}
那麼,對應的 Loop 重載是這種
public static LoopExpression Loop(Expression body)
使用表達式樹編寫
BlockExpression _block = Expression.Block(
new ParameterExpression[] { },
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),Expression.Constant("無限迴圈") )
);
LoopExpression _loop = Expression.Loop(_block);
Expression<Action> lambda = Expression.Lambda<Action>(_loop);
lambda.Compile()();
最簡單的迴圈
如果我想用表達式樹做到如下最簡單的迴圈,怎麼寫?
while (true)
{
Console.WriteLine("我被執行一次就結束迴圈了");
break;
}
表達式樹編寫
LabelTarget _break = Expression.Label();
BlockExpression _block = Expression.Block(
new ParameterExpression[] { },
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("我被執行一次就結束迴圈了")), Expression.Break(_break));
LoopExpression _loop = Expression.Loop(_block, _break);
Expression<Action> lambda = Expression.Lambda<Action>(_loop);
lambda.Compile()();
Console.ReadKey();
生成的表達式樹
.Lambda #Lambda1<System.Action>() {
.Loop {
.Block() {
.Call System.Console.WriteLine("我被執行一次就結束迴圈了");
.Break #Label1 { }
}
}
.LabelTarget #Label1:
}
首先要明確,Expression.Label()
裡面可以為空,它是一種標記,不參與傳遞參數,不參與運算。有參無參,前後保持一致即可。
但是上面的迴圈只有一次,你可以將上面的標簽改成這樣試試 LabelTarget _break = Expression.Label(typeof(int));
,原因後面找。
還有, Expression.Label() 變數需要一致,否則無法跳出。
試試一下代碼
BlockExpression _block = Expression.Block(
new ParameterExpression[] { },
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("我被執行一次就結束迴圈了")), Expression.Break(Expression.Label()));
LoopExpression _loop = Expression.Loop(_block, Expression.Label());
Expression<Action> lambda = Expression.Lambda<Action>(_loop);
lambda.Compile()();
Console.ReadKey();
裡面用到了 Expression.Block(),Block() 是塊,即{}。
如果 Block() 是在最外層,那麼相當於是函數;如果是內嵌,相當於{};
但不是真的這樣。。。表達式樹裡面不是完全按照 C# 的語法來還原操作的。
對於 Block() 的使用,多加實踐即可。
多次迴圈
寫一個迴圈十次的迴圈語句
for (int i = 0; i < 10; i++)
{
if (i < 10)
{
Console.WriteLine(i);
}
else
break;
}
或者使用 while 表示
int i = 0;
while (true)
{
if (i < 10)
{
Console.WriteLine(i);
}
else
break;
i++;
}
使用表達式樹編寫
LabelTarget _break = Expression.Label(typeof(int));
ParameterExpression a = Expression.Variable(typeof(int), "a");
BlockExpression _block = Expression.Block(new ParameterExpression[] { },
Expression.IfThenElse
(
Expression.LessThan(a, Expression.Constant(10)),
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
Expression.Break(_break, a)
),
Expression.PostIncrementAssign(a) // a++
);
LoopExpression _loop = Expression.Loop(_block, _break);
Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(_loop, a);
lambda.Compile()(0);
Console.ReadKey();
生成的表達式樹如下
.Lambda #Lambda1<System.Action`1[System.Int32]>(System.Int32 $a) {
.Loop {
.Block() {
.If ($a < 10) {
.Call System.Console.WriteLine($a)
} .Else {
.Break #Label1 { $a }
};
$a++
}
}
.LabelTarget #Label1:
}
試試將 Expression.Break(_break, a)
改成 Expression.Break(_break)
。看看報什麼錯。。。
解決方法是,上面的標記也改成 LabelTarget _break = Expression.Label();
。
就跟你寫代碼寫註釋一樣,裡面的東西是為了讓別人看代碼是容易理解。
有些同學糾結於 Expression.Label(有參或無參);
,Expression.Break(_break, a)
與 Expression.Break(_break)
,只要看看最終生成的表達式樹就清楚了。
break 和 continue 一起
C# 迴圈代碼如下
int i = 0;
while (true)
{
if (i < 10)
{
if (i % 2 == 0)
{
Console.Write("i是偶數:");
Console.WriteLine(i);
i++;
continue;
}
Console.WriteLine("其他任務 --");
Console.WriteLine("其他任務 --");
}
else break;
i++;
}
使用 C# 表達式樹編寫(筆者將步驟詳細拆分了,所以代碼比較長)
ParameterExpression a = Expression.Variable(typeof(int), "a");
LabelTarget _break = Expression.Label();
LabelTarget _continue = Expression.Label();
// if (i % 2 == 0)
// {
// Console.Write("i是偶數:");
// Console.WriteLine(i);
// i++;
// continue;
// }
ConditionalExpression _if = Expression.IfThen(
Expression.Equal(Expression.Modulo(a, Expression.Constant(2)), Expression.Constant(0)),
Expression.Block(
new ParameterExpression[] { },
Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("i是偶數:")),
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
Expression.PostIncrementAssign(a),
Expression.Continue(_continue)
)
);
// if (i % 2 == 0)
// {
// Console.Write("i是偶數:");
// Console.WriteLine(i);
// i++;
// continue;
// }
// Console.WriteLine("其他任務 --");
// Console.WriteLine("其他任務 --");
BlockExpression block1 = Expression.Block(
new ParameterExpression[] { },
_if,
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任務 --")),
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任務 --"))
);
// if (i < 10)
// {
// if (i % 2 == 0)
// {
// Console.Write("i是偶數:");
// Console.WriteLine(i);
// i++;
// continue;
// }
// Console.WriteLine("其他任務 --");
// Console.WriteLine("其他任務 --");
// }
// else break;
ConditionalExpression if_else = Expression.IfThenElse(
Expression.LessThan(a, Expression.Constant(10)),
block1,
Expression.Break(_break)
);
// if (i < 10)
// {
// if (i % 2 == 0)
// {
// Console.Write("i是偶數:");
// Console.WriteLine(i);
// i++;
// continue;
// }
// Console.WriteLine("其他任務 --");
// Console.WriteLine("其他任務 --");
// }
// else break;
// i++ ;
BlockExpression block2 = Expression.Block(
new ParameterExpression[] { },
if_else,
Expression.PostIncrementAssign(a)
);
// while(true)
LoopExpression loop = Expression.Loop(block2, _break, _continue);
Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(loop, a);
lambda.Compile()(0);
Console.ReadKey();
生成的表達式樹如下
.Lambda #Lambda1<System.Action`1[System.Int32]>(System.Int32 $a) {
.Loop .LabelTarget #Label1: {
.Block() {
.If ($a < 10) {
.Block() {
.If (
$a % 2 == 0
) {
.Block() {
.Call System.Console.Write("i是偶數:");
.Call System.Console.WriteLine($a);
$a++;
.Continue #Label1 { }
}
} .Else {
.Default(System.Void)
};
.Call System.Console.WriteLine("其他任務 --");
.Call System.Console.WriteLine("其他任務 --")
}
} .Else {
.Break #Label2 { }
};
$a++
}
}
.LabelTarget #Label2:
}
為了便於理解,上面的代碼拆分了很多步。
來個簡化版本
ParameterExpression a = Expression.Variable(typeof(int), "a");
LabelTarget _break = Expression.Label();
LabelTarget _continue = Expression.Label();
LoopExpression loop = Expression.Loop(
Expression.Block(
new ParameterExpression[] { },
Expression.IfThenElse(
Expression.LessThan(a, Expression.Constant(10)),
Expression.Block(
new ParameterExpression[] { },
Expression.IfThen(
Expression.Equal(Expression.Modulo(a, Expression.Constant(2)), Expression.Constant(0)),
Expression.Block(
new ParameterExpression[] { },
Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("i是偶數:")),
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
Expression.PostIncrementAssign(a),
Expression.Continue(_continue)
)
),
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任務 --")),
Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任務 --"))
),
Expression.Break(_break)
),
Expression.PostIncrementAssign(a)
),
_break,
_continue
);
Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(loop, a);
lambda.Compile()(0);
Console.ReadKey();
需要註意的是,Expression.Break
Expression.Continue
有所區別。
當標簽實例化都是 Expression.Label()
時,
Expression.Break(label);
Expression.Continu(label);
區別在於 continu 只能用 Expression.Label()。
Break 可以這樣
LabelTarget label = Expression.Label ( typeof ( int ) );
ParameterExpression a = Expression.Variable(typeof(int), "a");
Expression.Break ( label , a )