/// <summary>
        /// Given a single item function that should be a static method, this builds a serialized version of
        /// that method that should be enough to "recover" it, assuming it is a "recoverable" method (recoverable
        /// here is a loose definition, meaning that <see cref="DeserializeStaticDelegateOrNull"/> is capable
        /// of creating it, which includes among other things that it's static, non-lambda, accessible to
        /// this assembly, etc.).
        /// </summary>
        /// <param name="func">The method that should be "recoverable"</param>
        /// <returns>A string array describing the input method</returns>
        public static byte[] GetSerializedStaticDelegate(ExtLambdaTransform.LoadDelegate func)
        {
            Contracts.CheckValue(func, nameof(func));
            Contracts.CheckParam(func.Target == null, nameof(func), "The load delegate must be static");
            Contracts.CheckParam(Utils.Size(func.GetInvocationList()) <= 1, nameof(func),
                                 "The load delegate must not be a multicast delegate");

            var meth = func.GetMethodInfo();

            using (var ms = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
#if CORECLR
                var m = new CoreHackMethodInfo();
                m.AssemblyName = meth.Module.Assembly.FullName;
                m.MethodName   = meth.Name;
                m.ClassName    = meth.DeclaringType.ToString();
                formatter.Serialize(ms, m);
#else
                formatter.Serialize(ms, meth);
#endif
                var result = ms.ToArray();
                // I assume it must be impossible to serialize in 0 bytes.
                Contracts.Assert(Utils.Size(result) > 0);
                return(result);
            }
        }
        /// <summary>
        /// Create a filter transform that is savable iff <paramref name="saveAction"/> and <paramref name="loadFunc"/> are
        /// not null.
        /// </summary>
        /// <param name="env">The host environment</param>
        /// <param name="source">The dataview upon which we construct the transform</param>
        /// <param name="filterFunc">The function by which we transform source to destination columns and decide whether
        /// to keep the row.</param>
        /// <param name="initStateAction">The function that is called once per cursor to initialize state. Can be null.</param>
        /// <param name="saveAction">An action that allows us to save state to the serialization stream. May be
        /// null simultaneously with <paramref name="loadFunc"/>.</param>
        /// <param name="loadFunc">A function that given the serialization stream and a data view, returns
        /// an <see cref="ITransformTemplate"/>. The intent is, this returned object should itself be a
        /// <see cref="CustomMappingTransformer{TSrc,TDst}"/>, but this is not strictly necessary. This delegate should be
        /// a static non-lambda method that this assembly can legally call. May be null simultaneously with
        /// <paramref name="saveAction"/>.</param>
        /// <param name="inputSchemaDefinition">The schema definition overrides for <typeparamref name="TSrc"/></param>
        /// <param name="outputSchemaDefinition">The schema definition overrides for <typeparamref name="TDst"/></param>
        public ExtStatefulFilterTransform(IHostEnvironment env, IDataView source, Func <TSrc, TDst, TState, bool> filterFunc,
                                          Action <TState> initStateAction,
                                          Action <BinaryWriter> saveAction, ExtLambdaTransform.LoadDelegate loadFunc,
                                          SchemaDefinition inputSchemaDefinition = null, SchemaDefinition outputSchemaDefinition = null)
            : base(env, RegistrationName, saveAction, loadFunc)
        {
            Host.AssertValue(source, "source");
            Host.AssertValue(filterFunc, "filterFunc");
            Host.AssertValueOrNull(initStateAction);
            Host.AssertValueOrNull(inputSchemaDefinition);
            Host.AssertValueOrNull(outputSchemaDefinition);

            _source                = source;
            _filterFunc            = filterFunc;
            _initStateAction       = initStateAction;
            _inputSchemaDefinition = inputSchemaDefinition;
            _typedSource           = TypedCursorable <TSrc> .Create(Host, Source, false, inputSchemaDefinition);

            var outSchema = InternalSchemaDefinition.Create(typeof(TDst), outputSchemaDefinition);

            _addedSchema = outSchema;
            _bindings    = new ColumnBindings(Source.Schema, DataViewConstructionUtils.GetSchemaColumns(outSchema));
        }
        protected ExtLambdaTransformBase(IHostEnvironment env, string name, Action <BinaryWriter> saveAction, ExtLambdaTransform.LoadDelegate loadFunc)
        {
            Contracts.AssertValue(env);
            env.AssertNonWhiteSpace(name);
            Host = env.Register(name);

            Host.Assert((saveAction == null) == (loadFunc == null));

            if (saveAction != null)
            {
                _saveAction = saveAction;
                // First, verify as best we can, that we can recover the function, by attempting to do it once.
                _loadFuncBytes = SerializableExtLambdaTransform.GetSerializedStaticDelegate(loadFunc);
                Exception error;
                var       recoveredLoadFunc = SerializableExtLambdaTransform.DeserializeStaticDelegateOrNull(Host, _loadFuncBytes, out error);
                if (recoveredLoadFunc == null)
                {
                    Host.AssertValue(error);
                    throw Host.Except(error, "Load function does not appear recoverable");
                }
            }

            AssertConsistentSerializable();
        }