如果没有微软的WF,如何设计一个自己的工作流,本章通过经典的芝麻开门(OpenSesame)示例,向读者展示了WF基本的设计思路.
这一章我读了很多遍才想明白.建议所有读者在阅读后续章节时,一定要先读懂本章,这是全书的基石;还有就是本章的示例代码并不完整,仅仅是一个类及其方法的骨架,也是容易使人困惑的.
设计交互式WF,要解决两个问题:
1.线程阻塞,比如说Console.ReadLine这样的语句, 因此有必要将其设计为异步方法BeginReadLine与EndReadLine,如下:
public static System.IAsyncResult BeginReadLine(System.AsyncCallback asyncCallback, object state);
public static string EndReadLine(System.IAsyncResult ar);
使用如下, 注意到变量key实现了跨线程共享:
static string key;

public static void Main()

{
key = "xxx";
BeginReadLine(ContinueAt, null);
Thread.Sleep(Timeout.Infinite);
}
static void ContinueAt(IAsyncResult ar)

{
string s = EndReadLine(ar);

if(key == s)
{}
}
不仅仅是ReadLine方法,所有阻塞线程的方法都可以一拆为二,并按照上述原则进行操作
2.进程闲置
闲置的进程可以序列化到外部,这个技术称为钝化passivate;需要的时候再进行恢复,恢复点称为书签Bookmark,程序上表现为delegate.
钝化可以彻底消除对栈的依赖.
Bookmark实体类设计如下:

注意, Name是key, Payload是异步调用时需要的参数,BookmarkLocation是一个委托,BookmarkManager用于指定当前Bookmark的管理器.
BookmarkManager是一个书签管理器,具体实现如下(书中省略了):
public class BookmarkManager

{
private List<Bookmark> bookmarkList;

public BookmarkManager()

{
bookmarkList = new List<Bookmark>();
}

public void Add(Bookmark bookmark)

{
bookmarkList.Add(bookmark);
bookmark.BookmarkManager = this;
}

public void Remove(Bookmark bookmark)

{
bookmarkList.Remove(bookmark);
}

public void Resume(string bookmarkName, object payload)

{
foreach (Bookmark bookmark in bookmarkList)

{
if (bookmark.Name == bookmarkName)

{
bookmark.Payload = payload;
bookmark.BookmarkLocation(bookmark);

break;
}
}
}
}
对于逻辑类OpenSesame的实现,要求有一个全局变量key和一个异步方法ContinueAt,这是为了解决线程阻塞,上文已经述及其意图;此外Start()方法也是必须的,用于初始化书签管理器并将自身加入其中,最后这个类还要标明[Serializable]
于是Main()方法的实现如下:
static void Main(string[] args)

{
BookmarkManager mgr = new BookmarkManager();
OpenSesame openSesameProgram = new OpenSesame();
openSesameProgram.Start(mgr);


string str = Console.ReadLine();
mgr.Resume("read", str);

Console.ReadLine();
}
至此,一个WF Sample完美实现,仅包括一个Sesame逻辑的一个实例。
接下来要考虑多个逻辑多个实例的实现:使用运行时Runtime进行管理
于是建立ProgramStatement抽象基类,所有逻辑类(如OpenSesame)都派生于此,其中Run()方法就是OpenSesame的Start()方法。我们称之为可恢复语句组件。
同时,建立MythicalRuntime类和PrograHandle类,进行更高层次的包装。

从而,Main()方法的实现可以如下:
MythicalRuntime runtime = new MythicalRuntime();

while (true)

{
Guid guid = "Your passive guid";
ProgramHandle handle = runtime.GetProgramHandle(guid);

string bookmarkName = "Jax";
object input = "XXXXXX";
handle.Resume(bookmarkName, input);
}
注意,这段代码的功能仅仅是根据input进行恢复,并没有事先设置初始值,我们只要看清大致思路即可。
MythicalRuntime类和ProgramHandle类,书中省略了代码,我自己的实现如下:
public class MythicalRuntime

{
Dictionary<ProgramHandle, ProgramStatement> ht;

public MythicalRuntime()

{
ht = new Dictionary<ProgramHandle, ProgramStatement>();
}

public ProgramHandle RunProgram(ProgramStatement program)

{
//这个新的Guid根据规则创建,而不是简单的new Guid(),以下仅为模拟方法
Guid programId = new Guid();

ProgramHandle programHandle = new ProgramHandle();
programHandle.ProgramId = programId;

ht.Add(programHandle, program);

return programHandle;
}

public ProgramHandle GetProgramHandle(Guid programId)

{
//根据programId恢复已经钝化的程序,假设恢复为OpenSesame方法
ProgramStatement program = new OpenSesame();

//重新构建ProgramHandle
ProgramHandle programHandle = new ProgramHandle();
programHandle.ProgramId = programId;

//重新加载到内存
ht.Add(programHandle, program);

return programHandle;
}

public void Shutdown()

{
//从内存中取出所有ProgramHandle, 依次钝化
foreach (ProgramHandle tmpProgramHandle in ht.Keys)

{
ProgramStatement program = ht[tmpProgramHandle];
tmpProgramHandle.Passivate(program);
}

ht = null;
}
}

public class ProgramHandle

{
private Guid programId;

public Guid ProgramId

{

get
{ return programId; }

set
{ programId = value; }
}

public void Passivate(ProgramStatement program)

{
//将program根据关键字programId进行钝化
}

public void Resume(string bookmarkName, object payload)

{
BookmarkManager mgr = new BookmarkManager();
mgr.Resume(bookmarkName, payload);
}
}
根据我的理解,做出如下修正:
1.在MythicalRuntime类中添加泛型ht,建立了ProgramHandle与ProgramStatement的一一映射。
2.RunProgram专门用于在内存中创建新程序。
3.ProgramHandle类的Passivate()方法,增加方法参数后为:
public void passive(ProgramStatement program);
从而将programId和相应的ProgramStatement一起存储。
*本章的页眉都写错了,应该把"部"改为"剖",建议译者下一版时修正.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架