Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Chinese readme #568

Open
YooungShang opened this issue Mar 23, 2024 · 3 comments
Open

Add Chinese readme #568

YooungShang opened this issue Mar 23, 2024 · 3 comments

Comments

@YooungShang
Copy link

YooungShang commented Mar 23, 2024

for example:

Stateless Build status NuGet Pre Release Join the chat at https://gitter.im/dotnet-state-machine/stateless Stack Overflow

直接在.NET代码中创建状态机和基于轻量级状态机的工作流:

var phoneCall = new StateMachine<State, Trigger>(State.OffHook);

phoneCall.Configure(State.OffHook)
    .Permit(Trigger.CallDialled, State.Ringing);

phoneCall.Configure(State.Connected)
    .OnEntry(t => StartCallTimer())
    .OnExit(t => StopCallTimer())
    .InternalTransition(Trigger.MuteMicrophone, t => OnMute())
    .InternalTransition(Trigger.UnmuteMicrophone, t => OnUnmute())
    .InternalTransition<int>(_setVolumeTrigger, (volume, t) => OnSetVolume(volume))
    .Permit(Trigger.LeftMessage, State.OffHook)
    .Permit(Trigger.PlacedOnHold, State.OnHold);

// ...

phoneCall.Fire(Trigger.CallDialled);
Assert.AreEqual(State.Ringing, phoneCall.State);

这个项目,以及上面的例子,是受到简单状态机(存档)的启发。

特性

支持大多数标准状态机结构:

  • 对任何.NET类型(数字、字符串、枚举等)的状态和触发器的通用支持
  • Hierarchical states
  • 状态的进入/退出动作
  • 保护子句来支持条件转换
  • 内省

还提供了一些有用的扩展:

  • 能够在外部存储状态(例如,在由ORM跟踪的属性中)
  • 参数化的触发器
  • 可重入状态
  • 导出到DOT图

状态分层

在下面的例子中,OnHold状态是Connected状态的子状态。这意味着OnHold呼叫仍然处于连接状态。

phoneCall.Configure(State.OnHold)
    .SubstateOf(State.Connected)
    .Permit(Trigger.TakenOffHold, State.Connected)
    .Permit(Trigger.PhoneHurledAgainstWall, State.PhoneDestroyed);

除了StateMacheine.State属性之外,IsInstate(State)也可以精确的报告当前状态。

IsInStaet(State)也会考虑子状态,所以如果上文中的例子处于OnHold状态,IsInState(State.Connected)的结果也会为true

进入/退出动作

在这个例子中,StartCallTimer()方法将在呼叫连接时执行。StopCallTimer()将在呼叫完成时执行。

呼叫可以在ConnectedOnHold状态之间移动,而不需要重复调用StartCallTimer()StopCallTimer()方法,因为OnHold状态是Connected状态的子状态。

进入/退出动作处理程序可以使用Transition类型的参数提供,该参数描述触发器、源和目标状态。

内部转换

有时需要处理触发器,但状态不应该改变。这是一种内部转变。使用InternalTransition

初始状态转换

子状态可以标记为初始状态。当状态机进入超级状态时,它也会自动进入子状态。可以这样配置:

    sm.Configure(State.B)
        .InitialTransition(State.C);

    sm.Configure(State.C)
        .SubstateOf(State.B);

由于Stateless的内部结构,它不知道何时启动。这使得无法以传统方式处理初始转换。可以通过添加一个虚拟初始状态来绕过这个限制,然后使用Activate()启动状态机。

    sm.Configure(InitialState)
        .OnActivate(() => sm.Fire(LetsGo))
        .Permit(LetsGo, StateA)

外部状态存储器

Stateless被设计为嵌入到各种应用程序模型中。例如,一些ORM对可能存储映射数据的位置提出了要求,UI框架通常要求将状态存储在特殊的可绑定属性中。为此,StateMachine构造函数可以接受用于读写状态值的函数参数:

var stateMachine = new StateMachine<State, Trigger>(
    () => myState.Value,
    s => myState.Value = s);

在本例中,状态机将使用myState对象进行状态存储。

另一个示例可以在位于example文件夹中的JsonExample解决方案中找到。

激活/取消激活

在存储对象状态之前可能需要执行一些代码,在恢复对象状态时也是如此。使用DeactivateActivate。激活应该只在正常操作开始之前调用一次,在状态存储之前调用一次。

自省

状态机可以通过StateMachine.PermittedTriggers 属性提供可以在当前状态下成功触发的触发器列表。使用 StateMachine.GetInfo() 检索有关状态配置的信息。

保护子句

状态机将根据保护子句在多个转换之间进行选择,例如:

phoneCall.Configure(State.OffHook)
    .PermitIf(Trigger.CallDialled, State.Ringing, () => IsValidNumber)
    .PermitIf(Trigger.CallDialled, State.Beeping, () => !IsValidNumber);

在一个状态内的保护子句必须是互斥的(多个保护子句不能同时有效)。子状态可以通过重新指定它们来覆盖转换,但子状态不能禁止超状态允许的转换。

每当触发器被触发时,将评估保护子句。因此,应该使保护子句无副作用。

参数化的触发器

强类型参数可以分配给触发器:

var assignTrigger = stateMachine.SetTriggerParameters<string>(Trigger.Assign);

stateMachine.Configure(State.Assigned)
    .OnEntryFrom(assignTrigger, email => OnAssigned(email));

stateMachine.Fire(assignTrigger, "joe@example.com");

触发器参数可用于使用PermitDynamic()配置方法动态选择目标状态。

忽略转换和可重入状态

触发没有关联允许转换的触发器将导致抛出异常。

要忽略某些状态下的触发器,使用ignore (TTrigger)指令:

phoneCall.Configure(State.Connected)
    .Ignore(Trigger.CallDialled);

或者,一个状态可以被标记为可重入的,这样它的进入和退出动作即使转换是来源或目标是自身时也会触发:

stateMachine.Configure(State.Assigned)
    .PermitReentry(Trigger.Assigned)
    .OnEntry(() => SendEmailToAssignee());

默认情况下,必须显式忽略触发器。要覆盖Stateless在触发未处理触发器时抛出异常的默认行为,请使用OnUnhandledTrigger方法配置状态机:

stateMachine.OnUnhandledTrigger((state, trigger) => { });

状态更改通知(事件)

Stateless支持两种类型的状态机事件:

  • 状态转换
  • 状态转换完成

状态转换

stateMachine.OnTransitioned((transition) => { });

此事件将在每次状态机更改状态时调用。

状态机转换完成

stateMachine.OnTransitionCompleted((transition) => { });

在触发器处理的最后一步,在最后一个进入动作之后调用此事件。

导出到DOT图

在运行时可视化状态机是很有用的。使用这种方法,代码是权威的来源,状态图是总是最新的副产品。

phoneCall.Configure(State.OffHook)
    .PermitIf(Trigger.CallDialled, State.Ringing, IsValidNumber);
    
string graph = UmlDotGraph.Format(phoneCall.GetInfo());

UmlDotGraph.Format()方法以DOT图形语言的形式返回状态机的字符串表示,例如:

digraph {
  OffHook -> Ringing [label="CallDialled [IsValidNumber]"];
}

然后可以通过支持DOT图形语言的工具来呈现,例如来自graphviz.orgviz.jsDOT命令行工具。请参阅http://www.webgraphviz.com/ 获取即时满足。
命令行示例:dot -T pdf -o phoneCall.pdf phoneCall.dot以生成PDF文件。

异步触发

在提供Task<T>的平台上,StateMachine支持async进入/退出动作等等:

stateMachine.Configure(State.Assigned)
    .OnEntryAsync(async () => await SendEmailToAssignee());

在这些情况下,异步处理程序必须使用*Async()方法注册。

要触发调用异步动作的触发器,必须使用FireAsync()方法:

await stateMachine.FireAsync(Trigger.Assigned);

注意:虽然StateMachine可以被_异步的_使用,但它仍然是单线程的,不能被多个线程_同步的_使用。

其他功能

保留SynchronizationContext

在特定的情况下,所有的处理程序方法都必须用消费者的SynchronizationContext来调用,在创建时设置RetainSynchronizationContext属性:

var stateMachine = new StateMachine<State, Trigger>(initialState)
{
    RetainSynchronizationContext = true
};

例如,在Microsoft Orleans Grain中设置这个是至关重要的,它需要SynchronizationContext来调用其他Grain。

生成

Stateless 可以在 .NET Framework 4.6.2、.NET Standard 2.0 和 .NET 8.0,在 .NET 运行时版本 4+ 和几乎所有现代 .NET 平台上运行。需要 Visual Studio 2017 或更高版本才能生成解决方案

贡献

我们欢迎对这个项目的贡献。查看CONTRIBUTING.md了解更多信息。

项目目标

本页几乎是对Stateless的完整描述,其明确的目标是保持最小化。

如果您想报告问题或讨论功能,请使用问题跟踪器或讨论页面。

(为什么取这个名字?Stateless实现了一组关于状态转换的规则,但是,至少在使用构造函数的委托版本时,它本身不维护任何内部状态。)

@HenningNT
Copy link
Contributor

This is a great suggestion, but I do not think we'd be able to keep it updated, other than relying on auto-translated documentation. The risk is that we then have poor Chinese documentation. At the moment I think it's better to have only English documentation, rather than English and poorly translated (or outdated) Chinese documentation.

@YooungShang
Copy link
Author

This is a great suggestion, but I do not think we'd be able to keep it updated, other than relying on auto-translated documentation. The risk is that we then have poor Chinese documentation. At the moment I think it's better to have only English documentation, rather than English and poorly translated (or outdated) Chinese documentation.

我是中国人,可以提供汉化帮助。
I am Chinese and can help with Sinicization.

@YooungShang
Copy link
Author

This is a great suggestion, but I do not think we'd be able to keep it updated, other than relying on auto-translated documentation. The risk is that we then have poor Chinese documentation. At the moment I think it's better to have only English documentation, rather than English and poorly translated (or outdated) Chinese documentation.

上面的翻译,就是我基于机器翻译修改的
The above translation is modified by me based on machine translation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants