最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Dynamically bindapply funcs in transformation pipeline - Stack Overflow

programmeradmin1浏览0评论

I am attempting to build a dynamic transformation pipeline driven by config.

I have a collection of static methods which return Func<SourceType,TargetType> delegates, of the following format. The idea being that the parameters I pass into these methods (s1 in the example below) are parameters. The Func itself will then take a source object and convert to target object.

        public static Func<string, Result<string>> Concat(string s1) => (string s) =>
            Result<string>.Success(s1 + s);

I will not not know the source / target types until runtime (they will be read from a config json).

However, I want to be able to chain funcs and have some form of type checking that the function being called is compatible with the object being returned from the previous func.

My current approach is as follows:

I have a method that attempts to return a Func<T, Result> but this is invalid as I cannot cast an object type return from the invoked method to a Func<T,Result> due to no covariance.

GetConverterMethod - returns a Func<T,Result> (where T and U are non generic) based on the name of the function picked up from a config file.

AssignParameters - returns an object array with values picked up from config matching the converter signature. I.e. for concat it would return an array with a single element corresponding to "s1".

      public static Func<T,Result<object>> GetFunc<T>(T sourceObject, string methodName, Dictionary<string, object> paramsDict)
      {
          MethodInfo method = GetConverterMethod(sourceObject.GetType(), methodName);
          object[] assignedParameters = AssignParameters(method, paramsDict);

          return (Func<T, Result<object>>)method.Invoke(null, assignedParameters);
      }

My plan was to apply the functions as follows in an execution pipeline:

      public static object ExecutePipeline<T>(T input, List<TransformationStep> steps)
      {
          object result = input;

          foreach (var step in steps)
          {
              var func = GetFunc(result, step.Name, step.Parameters );
              var funcResult = func.DynamicInvoke(result);

              if(funcResult is Result<object> resultObject && resultObject.IsSuccess)
              {
                  result = resultObject.Value;
              }
              else
              {
                  throw new Exception();
              }
          }
          return result;
      }

My question is whether there is a cleaner approach / functional pattern that can handle calling funcs were the intermediate types are not known until runtime.

Note that Result is a simple Result monad:

    public class Result<T>
    {
        public bool IsSuccess { get; }
        public T Value { get; }
        public string Error { get; }

        private Result(T value, bool isSuccess, string error)
        {
            this.IsSuccess = isSuccess;
            this.Value = value;
            this.Error = error;
        }

        public static Result<T> Success(T value) => new Result<T>(value, true, null);
        public static Result<T> Failure(string error) => new Result<T>(default, false, error);

        public Result<U> Bind<U>(Func<T, Result<U>> func)
        {
            return this.IsSuccess ? func(this.Value) : Result<U>.Failure(this.Error);
        }

        public static implicit operator Result<T>(T value) => Success(value);
        public static explicit operator T(Result<T> result) => result.Value;
    }
发布评论

评论列表(0)

  1. 暂无评论
ok 不同模板 switch ($forum['model']) { /*case '0': include _include(APP_PATH . 'view/htm/read.htm'); break;*/ default: include _include(theme_load('read', $fid)); break; } } break; case '10': // 主题外链 / thread external link http_location(htmlspecialchars_decode(trim($thread['description']))); break; case '11': // 单页 / single page $attachlist = array(); $imagelist = array(); $thread['filelist'] = array(); $threadlist = NULL; $thread['files'] > 0 and list($attachlist, $imagelist, $thread['filelist']) = well_attach_find_by_tid($tid); $data = data_read_cache($tid); empty($data) and message(-1, lang('data_malformation')); $tidlist = $forum['threads'] ? page_find_by_fid($fid, $page, $pagesize) : NULL; if ($tidlist) { $tidarr = arrlist_values($tidlist, 'tid'); $threadlist = well_thread_find($tidarr, $pagesize); // 按之前tidlist排序 $threadlist = array2_sort_key($threadlist, $tidlist, 'tid'); } $allowpost = forum_access_user($fid, $gid, 'allowpost'); $allowupdate = forum_access_mod($fid, $gid, 'allowupdate'); $allowdelete = forum_access_mod($fid, $gid, 'allowdelete'); $access = array('allowpost' => $allowpost, 'allowupdate' => $allowupdate, 'allowdelete' => $allowdelete); $header['title'] = $thread['subject']; $header['mobile_link'] = $thread['url']; $header['keywords'] = $thread['keyword'] ? $thread['keyword'] : $thread['subject']; $header['description'] = $thread['description'] ? $thread['description'] : $thread['brief']; $_SESSION['fid'] = $fid; if ($ajax) { empty($conf['api_on']) and message(0, lang('closed')); $apilist['header'] = $header; $apilist['extra'] = $extra; $apilist['access'] = $access; $apilist['thread'] = well_thread_safe_info($thread); $apilist['thread_data'] = $data; $apilist['forum'] = $forum; $apilist['imagelist'] = $imagelist; $apilist['filelist'] = $thread['filelist']; $apilist['threadlist'] = $threadlist; message(0, $apilist); } else { include _include(theme_load('single_page', $fid)); } break; default: message(-1, lang('data_malformation')); break; } ?>