擴展C#與元編程(二)


如果你對Windows Workflow Foundation(WF)一無所知,當看到擴展C#與元編程(一)中由MW編譯器生成的FirstLook.mw.cs時,也許這么在想:我KAO,這是C#版的匯編語言!
WF到底是什么?可以這么認為:WF runtime是高級版的CLR(CLR上的CLR),activity是高級版的MSIL指令(可以勉強這么比喻),Metah.W是高級版的C#。
Activity可以分為兩類:primitive activity和composite activity。Primitive activity繼承自System.Activities.NativeActivity, System.Activities.NativeActivity<T>等,用來實現流程控制容器,如sequence, if-else, while, foreach, try-catch-finally等:

namespace System.Activities.Statements {
    public sealed class Sequence : NativeActivity {
        public Sequence();
        public Collection<Variable> Variables { get; }
        public Collection<Activity> Activities { get; }
        //...
    }

    public sealed class If : NativeActivity {
        public If();
        public InArgument<bool> Condition { get; set; }
        public Activity Then { get; set; }
        public Activity Else { get; set; }
        //...
    }

    public sealed class While : NativeActivity {
        public While();
        public Collection<Variable> Variables { get; }
        public Activity<bool> Condition { get; set; }
        public Activity Body { get; set; }
        //...
    }

    public sealed class ForEach<T> : NativeActivity {
        public ForEach();
        public InArgument<IEnumerable<T>> Values { get; set; }
        public ActivityAction<T> Body { get; set; }
        //...
    }
    
    public sealed class TryCatch : NativeActivity {
        public TryCatch();
        public Activity Try { get; set; }
        public Collection<Catch> Catches { get; }
        public Activity Finally { get; set; }
        //...
    }
}

WF這個高級CLR令人拍手稱快的特性之一是,你可以自定義流程控制容器(可以想象成自定義MSIL指令),比如高大上的狀態機:

namespace System.Activities.Statements {
    public sealed class StateMachine : NativeActivity {
        public StateMachine();
        public Collection<Variable> Variables { get; }
        public State InitialState { get; set; }
        public Collection<State> States { get; }
        //...
    }
    public sealed class State {
        public State();
        public bool IsFinal { get; set; }
        public Collection<Variable> Variables { get; }
        public Activity Entry { get; set; }
        public Activity Exit { get; set; }
        public Collection<Transition> Transitions { get; }
        //...
    }
    public sealed class Transition {
        public Transition();
        public Activity Trigger { get; set; }
        public Activity<bool> Condition { get; set; }
        public Activity Action { get; set; }
        public State To { get; set; }
        //...
    }
}

SecondLook.mw展示了如何使用狀態機:

一個statemachine至少包含一個common state及一個final state,每個state由唯一的label標識,如InPark, InNeutral等,break關鍵字標明這是一個final state,statemachine關鍵字后的goto clause標明initial common state,當流程進入某common state后,首先執行~>所標識的entry statement,接着執行on關鍵字標識的trigger statement,然后依順序評估if關鍵字標識的condition expression,如果某condition評估為true,則執行<~所標識的exit statement和do關鍵字標識的action statement(SecondLook.mw中未使用),接着跳轉到goto關鍵字所標識的state,如果所有的condition expression都評估為false,則重新執行該state的trigger statement。如果某final state執行完畢,則該statemachine執行完畢。
MW編譯器將activity Drive翻譯成下面的C#代碼:

//SecondLook.mw.cs, generated by MW compiler
namespace HelloMW.SecondLook
{
    class Drive : global::System.Activities.Activity
    {
        private global::System.Activities.Activity __GetImplementation__()
        {
            global::System.Activities.Activity __vroot__;
            {
                var __v__0 = new global::System.Activities.Statements.Sequence();
                var isMoved = new global::System.Activities.Variable<bool>();
                __v__0.Variables.Add(isMoved);
                __v__0.Activities.Add(new global::MetahWActionActivity(__ctx__ =>
                {
                    isMoved.SetEx(__ctx__, false);
                }

                ));
                var __v__1 = new global::System.Activities.Statements.While();
                __v__1.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => !isMoved.Get(__ctx__));
                {
                    var __v__2 = new global::System.Activities.Statements.Sequence();
                    {
                        var __v__3 = new global::System.Activities.Statements.StateMachine();
                        var action = new global::System.Activities.Variable<DriveAction>();
                        __v__3.Variables.Add(action);
                        var __v__4 = new global::System.Activities.Statements.State();
                        var __v__5 = new global::System.Activities.Statements.Transition();
                        var __v__6 = new global::System.Activities.Statements.State();
                        var __v__7 = new global::System.Activities.Statements.Transition();
                        var __v__8 = new global::System.Activities.Statements.Transition();
                        var __v__9 = new global::System.Activities.Statements.Transition();
                        var __v__10 = new global::System.Activities.Statements.State();
                        var __v__11 = new global::System.Activities.Statements.Transition();
                        var __v__12 = new global::System.Activities.Statements.State();
                        var __v__13 = new global::System.Activities.Statements.Transition();
                        var __v__14 = new global::System.Activities.Statements.State();
                        {
                            __v__3.States.Add(__v__4);
                            __v__4.Entry = new global::MetahWActionActivity(__ctx__ =>
                            {
                                Console.WriteLine("Enter InPark");
                            }

                            );
                            __v__4.Exit = new global::MetahWActionActivity(__ctx__ =>
                            {
                                Console.WriteLine("Exit InPark");
                            }

                            );
                            __v__5.Trigger = new GetDriveAction().Initialize(__activity2__ =>
                            {
                                __activity2__.Result = new global::System.Activities.OutArgument<global::HelloMW.SecondLook.DriveAction>(new global::MetahWLocationActivity<global::HelloMW.SecondLook.DriveAction>(action));
                            }

                            );
                            __v__5.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.Neutral);
                            __v__4.Transitions.Add(__v__5);
                        }

                        {
                            __v__3.States.Add(__v__6);
                            __v__6.Entry = new global::MetahWActionActivity(__ctx__ =>
                            {
                                Console.WriteLine("Enter InNeutral");
                            }

                            );
                            __v__6.Exit = new global::MetahWActionActivity(__ctx__ =>
                            {
                                Console.WriteLine("Exit InNeutral");
                            }

                            );
                            __v__7.Trigger = new GetDriveAction().Initialize(__activity2__ =>
                            {
                                __activity2__.Result = new global::System.Activities.OutArgument<global::HelloMW.SecondLook.DriveAction>(new global::MetahWLocationActivity<global::HelloMW.SecondLook.DriveAction>(action));
                            }

                            );
                            __v__7.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.Forward);
                            __v__6.Transitions.Add(__v__7);
                            __v__8.Trigger = __v__7.Trigger;
                            __v__8.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.Reverse);
                            __v__6.Transitions.Add(__v__8);
                            __v__9.Trigger = __v__7.Trigger;
                            __v__9.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.TurnOff);
                            __v__6.Transitions.Add(__v__9);
                        }

                        {
                            __v__3.States.Add(__v__10);
                            __v__10.Entry = new global::MetahWActionActivity(__ctx__ =>
                            {
                                Console.WriteLine("Enter InForward");
                                isMoved.SetEx(__ctx__, true);
                            }

                            );
                            __v__10.Exit = new global::MetahWActionActivity(__ctx__ =>
                            {
                                Console.WriteLine("Exit InForward");
                            }

                            );
                            __v__11.Trigger = new GetDriveAction().Initialize(__activity2__ =>
                            {
                                __activity2__.Result = new global::System.Activities.OutArgument<global::HelloMW.SecondLook.DriveAction>(new global::MetahWLocationActivity<global::HelloMW.SecondLook.DriveAction>(action));
                            }

                            );
                            __v__11.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.Neutral);
                            __v__10.Transitions.Add(__v__11);
                        }

                        {
                            __v__3.States.Add(__v__12);
                            __v__12.Entry = new global::MetahWActionActivity(__ctx__ =>
                            {
                                Console.WriteLine("Enter InReverse");
                                isMoved.SetEx(__ctx__, true);
                            }

                            );
                            __v__12.Exit = new global::MetahWActionActivity(__ctx__ =>
                            {
                                Console.WriteLine("Exit InReverse");
                            }

                            );
                            __v__13.Trigger = new GetDriveAction().Initialize(__activity2__ =>
                            {
                                __activity2__.Result = new global::System.Activities.OutArgument<global::HelloMW.SecondLook.DriveAction>(new global::MetahWLocationActivity<global::HelloMW.SecondLook.DriveAction>(action));
                            }

                            );
                            __v__13.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => action.Get(__ctx__) == DriveAction.Neutral);
                            __v__12.Transitions.Add(__v__13);
                        }

                        {
                            __v__3.States.Add(__v__14);
                            __v__14.IsFinal = true;
                            __v__14.Entry = new global::MetahWActionActivity(__ctx__ =>
                            {
                                Console.WriteLine("TurnedOff");
                            }

                            );
                        }

                        __v__3.InitialState = __v__4;
                        __v__5.To = __v__6;
                        __v__7.To = __v__10;
                        __v__8.To = __v__12;
                        __v__9.To = __v__14;
                        __v__11.To = __v__6;
                        __v__13.To = __v__6;
                        __v__2.Activities.Add(__v__3);
                    }

                    __v__2.Activities.Add(new global::MetahWActionActivity(__ctx__ =>
                    {
                        Console.WriteLine("isMoved: " + isMoved.Get(__ctx__));
                    }

                    ));
                    __v__1.Body = __v__2;
                }

                __v__0.Activities.Add(__v__1);
                __vroot__ = __v__0;
            }

            return __vroot__;
        }

        private global::System.Func<global::System.Activities.Activity> __implementation__;
        protected override global::System.Func<global::System.Activities.Activity> Implementation
        {
            get
            {
                return __implementation__ ?? (__implementation__ = __GetImplementation__);
            }

            set
            {
                throw new global::System.NotSupportedException();
            }
        }
    }
}

“匯編語言代碼”總是冗長無趣的,所以有了Metah.W這樣的高級語言。下面是可能的執行結果:

Enter InPark
!action: Neutral
Exit InPark
Enter InNeutral
!action: Reverse
Exit InNeutral
Enter InReverse
!action: TurnOff
!action: Forward
!action: Neutral
Exit InReverse
Enter InNeutral
!action: Forward
Exit InNeutral
Enter InForward
!action: Forward
!action: Forward
!action: Reverse
!action: Neutral
Exit InForward
Enter InNeutral
!action: TurnOff
Exit InNeutral
TurnedOff
isMoved: True
請按任意鍵繼續. . .

Composite activity直接繼承自System.Activities.ActivitySystem.Activities.Activity<T>,它由其它primitive activity和/或composite activity組合而成。Metah.W和WF designer只能創作composite activity。

我不知道大家怎么看待微軟這家公司,多數時候MS是家專業但缺乏想象力的公司,有時它能創造出一些令人眼前一亮的作品,C#從第二版開始就一直閃亮,WF是個極富想象力的技術,不過,WF現在還是個藏在深山中的璞玉,希望此文能激起你研究WF的興趣。關於
Metah.W: A Workflow Metaprogramming的更多信息請訪問:https://github.com/knat/Metah
待續。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM