有没有办法避免使用副作用来处理这些数据

问题描述

我正在编写一个应用程序,它运行脚本插件自动用户过去必须通过串行终端手动执行的操作。所以,我基本上是在代码中实现串行终端的功能。终端的功能之一是发送一个命令,该命令启动终端从设备接收连续流数据,直到用户按下空格键,然后停止数据流。当数据流传输时,用户会在其他设备上的另一个应用程序中设置一些值,并观察终端中流传输的数据的变化。

现在,流数据可以采用不同的形状,具体取决于发送的特定命令。例如,一个响应可能如下所示:

---RESPONSE HEADER---
HERE: 1
ARE: 2 SOME:3
VALUES: 4
---RESPONSE HEADER---
HERE: 5
ARE: 6 SOME:7
VALUES: 8
....

一个可能看起来像:

here are  some values
in   cols and  rows
....

所以,我的想法是根据我发送的命令使用不同的解析器。所以,我做了以下事情:

public class Terminal 
{
   private SerialPort port;
   private IResponseHandler pollingResponseHandler;

   private object locker = new object();

   private List<Response1Clazz> response1;
   private List<Response2Clazz> response2;
   //setter omited for brevity
  //get snapshot of data at any point in time while response is polling.
   public List<Response1Clazz> Response1 {get { lock (locker) return new List<Response1Clazz>(response1); }
//setter omited for brevity
   public List<Response2Clazz> Response2  {get { lock (locker) return new List<Response1Clazz>(response2); }

   public Terminal()
   {
      port = new SerialPort(){/*initialize data*/}; //open port etc etc
          
   }
   
   void StartResponse1Polling()
   {
      Response1 = new List<Response1Clazz>();
      Parser<List<Response1Clazz>> parser = new keyvalueParser(Response1); //parser is of type T
      pollingResponseHandler = new PollingResponseHandler(parser);
      //write command to start polling response 1 in a task
   }

   void StartResponse2Polling()
   {
      Response2 = new List<Response2Clazz>();
      Parser<List<Response2Clazz>> parser = new RowColumnParser(Response2); //parser is of type T
      pollingResponseHandler = new PollingResponseHandler(parser); // this accepts a parser of type T
      //write command to start polling response 2
   }
   
   OnSerialDataReceived(object sender,Args a)
   {
      lock(locker){
       //do some processing yada yada
       //we pass in the serial data to the handler,which in turn delegates to the parser.
       pollingResponseHandler.Handle(processedSerialData);
     }       
   }
}

类的调用者将类似于

public class Plugin : BasePlugin
{
  public override void PluginMain()
  {
    Terminal terminal = new Terminal();
    terminal.StartResponse1Polling();
    //update some other data;
    Response1Clazz response = terminal.Response1;
    //process response
    //update more data
    response = terminal.Response1;
    //process response
    //terminal1.StopPolling();
    
  }
}

我的问题很笼统,但我想知道这是否是处理这种情况的最佳方法。现在我需要传入一个我想要修改的对象/列表,它是通过副作用修改的。出于某种原因,这感觉有点难看,因为代码中确实没有迹象表明这是正在发生的事情。我这样做纯粹是因为“开始”方法是知道要创建哪个解析器以及要更新哪些数据的位置。也许这是犹太洁食,但我认为值得询问是否有另一种/更好的方法。或者至少是一种更好的方式来表明“处理”方法会产生副作用。

谢谢!

解决方法

我在修改作为参数接收的 List<> 时没有发现问题。它不是世界上最美丽的东西,但它很常见。遗憾的是,C# 没有用于参数的 const 修饰符(将此与 C/C++ 进行比较,除非您将参数声明为 const,否则该方法可以对其进行修改)。您只需为参数指定一个不言自明的名称(例如 outputList),并在方法上添加注释(您知道,xml 注释块,例如 /// <param name="outputList">This list will receive...</param>)。

要给出更完整的响应,我需要查看整个代码。您省略了 Parser 的示例和 Handler 的示例。

相反,我发现您在 lock 中的 { lock (locker) return new List<Response1Clazz>(response1); } 有问题。考虑到您然后执行 Response1 = new List<Response1Clazz>();,但 Response1 只有一个 getter。