代码之家  ›  专栏  ›  技术社区  ›  thalm

创建可序列化匿名委托的替代方法

  •  2
  • thalm  · 技术社区  · 15 年前

    但是,当代理的用法总是很清楚的时候,有人能想出一个替代方法吗?

    我们有一个通用的create命令,它将委托作为构造函数中的参数。此委托将为create命令创建项:

    public class CreateCommand<T> : Command
    {
        public T Item;
        protected Func<T> Constructor;
    
        public ClientCreateCommand(Func<T> constructor)
        {
            Constructor = constructor;
        }
    
        public override void Execute()
        {
            Item = Constructor();
        }
    }
    

    命令的用法如下:

    var c = new CreateCommand<MyType>( () => Factory.CreateMyType(param1, param2, ...) );
    History.Insert(c);
    

    然后历史记录序列化命令并将其发送到服务器。ofc委托不能按原样序列化,我们得到一个异常。

    现在有人能想到一个非常简单的构造函数类,它可以被序列化,并且执行与lambda expression相同的任务吗?意味着它获取一个参数列表并返回一个类型为T的实例,然后我们可以这样写:

    var constructor = new Constructor<MyType>(param1, param2, ...);
    var c = new CreateCommand<MyType>(constructor);
    History.Insert(c);
    

    1 回复  |  直到 11 年前
        1
  •  2
  •   BenMorel Manish Pradhan    11 年前

    EDIT(2):我提供了两个完整的示例实现。它们分为“实施1”和“实施2”。

    你的代表本质上是一个工厂。您可以定义一个工厂接口并创建一个类,该类为您的Item类实现该接口。下面是一个例子:

    public interface IFactory<T>
    {
        T Create();
    }
    
    [Serializable]
    public class ExampleItemFactory : IFactory<T>
    {
        public int Param1 { get; set; }
    
        public string Param2 { get; set; }
    
        #region IFactory<T> Members
    
        public Item Create()
        {
            return new Item(this.Param1, this.Param2);
        }
    
        #endregion
    }
    
    public class CreateCommand<T> : Command
    {
        public T Item;
        protected IFactory<T> _ItemFactory;
    
        public CreateCommand(IFactory<T> factory)
        {
            _ItemFactory = factory;
        }
    
        public override void Execute()
        {
            Item = _ItemFactory.Create();
        }
    }
    

            IFactory<Item> itemFactory = new ExampleItemFactory { Param1 = 5, Param2 = "Example!" };
            CreateCommand<Item> command = new CreateCommand<Item>(itemFactory);
            command.Execute();
    

    编辑(1):的具体实现 IFactory<T> 您的应用程序需要什么将由您决定。您可以为需要的每个类创建特定的工厂类,也可以创建某种工厂,使用Activator.CreateInstance函数或使用某种控制反转框架(如Spring或StructureMap)动态创建实例。

    Assert语句确保一切都按预期进行。我运行这个应用程序时没有出错。

    实施1

    [Serializable]
    public abstract class Command
    {
        public abstract void Execute();
    }
    
    public class Factory
    {
        static Dictionary<Type, Func<object[], object>> _DelegateCache = new Dictionary<Type, Func<object[], object>>();
    
        public static void Register<T>(Func<object[], object> @delegate)
        {
            _DelegateCache[typeof(T)] = @delegate;
        }
    
        public static T CreateMyType<T>(params object[] args)
        {
            return (T)_DelegateCache[typeof(T)](args);
        }
    }
    
    public interface IFactory<T>
    {
        T Create();
    }
    
    [Serializable]
    public class CreateCommand<T> : Command
    {
        public T Item { get; protected set; }
        protected IFactory<T> _ItemFactory;
    
        public CreateCommand(IFactory<T> itemFactory)
        {
            this._ItemFactory = itemFactory;
        }
    
        public override void Execute()
        {
            this.Item = this._ItemFactory.Create();
        }
    }
    
    // This class is a base class that represents a factory capable of creating an instance using a dynamic set of arguments.
    [Serializable]
    public abstract class DynamicFactory<T> : IFactory<T>
    {
        public object[] Args { get; protected set; }
    
        public DynamicFactory(params object[] args)
        {
            this.Args = args;
        }
    
        public DynamicFactory(int numberOfArgs)
        {
            if (numberOfArgs < 0)
                throw new ArgumentOutOfRangeException("numberOfArgs", "The numberOfArgs parameter must be greater than or equal to zero.");
    
            this.Args = new object[numberOfArgs];
        }
    
        #region IFactory<T> Members
    
        public abstract T Create();
    
        #endregion
    }
    
    // This implementation uses the Activator.CreateInstance function to create an instance
    [Serializable]
    public class DynamicConstructorFactory<T> : DynamicFactory<T>
    {
        public DynamicConstructorFactory(params object[] args) : base(args) { }
    
        public DynamicConstructorFactory(int numberOfArgs) : base(numberOfArgs) { }
    
        public override T Create()
        {
            return (T)Activator.CreateInstance(typeof(T), this.Args);
        }
    }
    
    // This implementation uses the Factory.CreateMyType function to create an instance
    [Serializable]
    public class MyTypeFactory<T> : DynamicFactory<T>
    {
        public MyTypeFactory(params object[] args) : base(args) { }
    
        public MyTypeFactory(int numberOfArgs) : base(numberOfArgs) { }
    
        public override T Create()
        {
            return Factory.CreateMyType<T>(this.Args);
        }
    }
    
    [Serializable]
    class DefaultConstructorExample
    {
        public DefaultConstructorExample()
        {
        }
    }
    
    [Serializable]
    class NoDefaultConstructorExample
    {
        public NoDefaultConstructorExample(int a, string b, float c)
        {
        }
    }
    
    [Serializable]
    class PrivateConstructorExample
    {
        private int _A;
        private string _B;
        private float _C;
    
        private PrivateConstructorExample()
        {
        }
    
        public static void Register()
        {
            // register a delegate with the Factory class that will construct an instance of this class using an array of arguments
            Factory.Register<PrivateConstructorExample>((args) =>
                {
                    if (args == null || args.Length != 3)
                        throw new ArgumentException("Expected 3 arguments.", "args");
    
                    if (!(args[0] is int))
                        throw new ArgumentException("First argument must be of type System.Int32.", "args[0]");
    
                    if (!(args[1] is string))
                        throw new ArgumentException("Second argument must be of type System.String.", "args[1]");
    
                    if (!(args[2] is float))
                        throw new ArgumentException("Third argument must be of type System.Single.", "args[2]");
    
                    var instance = new PrivateConstructorExample();
    
                    instance._A = (int)args[0];
                    instance._B = (string)args[1];
                    instance._C = (float)args[2];
    
                    return instance;
                });
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var factory1 = new DynamicConstructorFactory<DefaultConstructorExample>(null);
            var command1 = new CreateCommand<DefaultConstructorExample>(factory1);
    
            var factory2 = new DynamicConstructorFactory<NoDefaultConstructorExample>(3);
            factory2.Args[0] = 5;
            factory2.Args[1] = "ABC";
            factory2.Args[2] = 7.1f;
            var command2 = new CreateCommand<NoDefaultConstructorExample>(factory2);
    
            PrivateConstructorExample.Register(); // register this class so that it can be created by the Factory.CreateMyType function
            var factory3 = new MyTypeFactory<PrivateConstructorExample>(3);
            factory3.Args[0] = 5;
            factory3.Args[1] = "ABC";
            factory3.Args[2] = 7.1f;
            var command3 = new CreateCommand<PrivateConstructorExample>(factory3);
    
            VerifySerializability<DefaultConstructorExample>(command1);
            VerifySerializability<NoDefaultConstructorExample>(command2);
            VerifySerializability<PrivateConstructorExample>(command3);
        }
    
        static void VerifySerializability<T>(CreateCommand<T> originalCommand)
        {
            var serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            using (var stream = new System.IO.MemoryStream())
            {
                System.Diagnostics.Debug.Assert(originalCommand.Item == null); // assert that originalCommand does not yet have a value for Item
                serializer.Serialize(stream, originalCommand); // serialize the originalCommand object
    
                stream.Seek(0, System.IO.SeekOrigin.Begin); // reset the stream position to the beginning for deserialization
    
                // deserialize
                var deserializedCommand = serializer.Deserialize(stream) as CreateCommand<T>;
                System.Diagnostics.Debug.Assert(deserializedCommand.Item == null); // assert that deserializedCommand still does not have a value for Item
                deserializedCommand.Execute();
                System.Diagnostics.Debug.Assert(deserializedCommand.Item != null); // assert that deserializedCommand now has a value for Item
            }
        }
    }
    

    Factory<T> 类来存储用于返回T类型实例的委托。

    [Serializable]
    public abstract class Command
    {
        public abstract void Execute();
    }
    
    [Serializable]
    public abstract class CreateCommand<T> : Command
    {
        public T Item { get; protected set; }
    }
    
    public class Factory<T>
    {
        private static readonly object _SyncLock = new object();
        private static Func<T> _CreateFunc;
        private static Dictionary<string, Func<T>> _CreateFuncDictionary;
    
        /// <summary>
        /// Registers a default Create Func delegate for type <typeparamref name="T"/>.
        /// </summary>
        public static void Register(Func<T> createFunc)
        {
            lock (_SyncLock)
            {
                _CreateFunc = createFunc;
            }
        }
    
        public static T Create()
        {
            lock (_SyncLock)
            {
                if(_CreateFunc == null)
                    throw new InvalidOperationException(string.Format("A [{0}] delegate must be registered as the default delegate for type [{1}]..", typeof(Func<T>).FullName, typeof(T).FullName));
    
                return _CreateFunc();
            }
        }
    
        /// <summary>
        /// Registers a Create Func delegate for type <typeparamref name="T"/> using the given key.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="createFunc"></param>
        public static void Register(string key, Func<T> createFunc)
        {
            lock (_SyncLock)
            {
                if (_CreateFuncDictionary == null)
                    _CreateFuncDictionary = new Dictionary<string, Func<T>>();
    
                _CreateFuncDictionary[key] = createFunc;
            }
        }
    
        public static T Create(string key)
        {
            lock (_SyncLock)
            {
                Func<T> createFunc;
                if (_CreateFuncDictionary != null && _CreateFuncDictionary.TryGetValue(key, out createFunc))
                    return createFunc();
                else
                    throw new InvalidOperationException(string.Format("A [{0}] delegate must be registered with the given key \"{1}\".", typeof(Func<T>).FullName, key));
            }
        }
    }
    
    [Serializable]
    public class CreateCommandWithDefaultDelegate<T> : CreateCommand<T>
    {
        public override void Execute()
        {
            this.Item = Factory<T>.Create();
        }
    }
    
    [Serializable]
    public class CreateCommandWithKeyedDelegate<T> : CreateCommand<T>
    {
        public string CreateKey { get; set; }
    
        public CreateCommandWithKeyedDelegate(string createKey)
        {
            this.CreateKey = createKey;
        }
    
        public override void Execute()
        {
            this.Item = Factory<T>.Create(this.CreateKey);
        }
    }
    
    [Serializable]
    class DefaultConstructorExample
    {
        public DefaultConstructorExample()
        {
        }
    }
    
    [Serializable]
    class NoDefaultConstructorExample
    {
        public NoDefaultConstructorExample(int a, string b, float c)
        {
        }
    }
    
    [Serializable]
    class PublicPropertiesExample
    {
        public int A { get; set; }
        public string B { get; set; }
        public float C { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            // register delegates for each type
            Factory<DefaultConstructorExample>.Register(() => new DefaultConstructorExample());
            Factory<NoDefaultConstructorExample>.Register(() => new NoDefaultConstructorExample(5, "ABC", 7.1f));
            Factory<PublicPropertiesExample>.Register(() => new PublicPropertiesExample() { A = 5, B = "ABC", C = 7.1f });
    
            // create commands
            var command1 = new CreateCommandWithDefaultDelegate<DefaultConstructorExample>();
            var command2 = new CreateCommandWithDefaultDelegate<DefaultConstructorExample>();
            var command3 = new CreateCommandWithDefaultDelegate<DefaultConstructorExample>();
    
            // verify that each command can be serialized/deserialized and that the creation logic works
            VerifySerializability<DefaultConstructorExample>(command1);
            VerifySerializability<DefaultConstructorExample>(command2);
            VerifySerializability<DefaultConstructorExample>(command3);
    
    
            // register additional delegates for each type, distinguished by key
            Factory<DefaultConstructorExample>.Register("CreateCommand", () => new DefaultConstructorExample());
            Factory<NoDefaultConstructorExample>.Register("CreateCommand", () => new NoDefaultConstructorExample(5, "ABC", 7.1f));
            Factory<PublicPropertiesExample>.Register("CreateCommand", () => new PublicPropertiesExample() { A = 5, B = "ABC", C = 7.1f });
    
            // create commands, passing in the create key to the constructor
            var command4 = new CreateCommandWithKeyedDelegate<DefaultConstructorExample>("CreateCommand");
            var command5 = new CreateCommandWithKeyedDelegate<DefaultConstructorExample>("CreateCommand");
            var command6 = new CreateCommandWithKeyedDelegate<DefaultConstructorExample>("CreateCommand");
    
            // verify that each command can be serialized/deserialized and that the creation logic works
            VerifySerializability<DefaultConstructorExample>(command4);
            VerifySerializability<DefaultConstructorExample>(command5);
            VerifySerializability<DefaultConstructorExample>(command6);
        }
    
        static void VerifySerializability<T>(CreateCommand<T> originalCommand)
        {
            var serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            using (var stream = new System.IO.MemoryStream())
            {
                System.Diagnostics.Debug.Assert(originalCommand.Item == null); // assert that originalCommand does not yet have a value for Item
                serializer.Serialize(stream, originalCommand); // serialize the originalCommand object
    
                stream.Seek(0, System.IO.SeekOrigin.Begin); // reset the stream position to the beginning for deserialization
    
                // deserialize
                var deserializedCommand = serializer.Deserialize(stream) as CreateCommand<T>;
                System.Diagnostics.Debug.Assert(deserializedCommand.Item == null); // assert that deserializedCommand still does not have a value for Item
                deserializedCommand.Execute();
                System.Diagnostics.Debug.Assert(deserializedCommand.Item != null); // assert that deserializedCommand now has a value for Item
            }
        }
    }