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

发布于 2024-09-10 08:14:48 字数 959 浏览 2 评论 0原文

有很多关于此的帖子,都试图序列化 Func 委托。

但是,当委托的使用总是很明确时,有人可以想到替代方案吗?

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

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 表达式执行相同的工作吗?意味着它需要一个参数列表并返回一个 T 类型的实例,然后我们可以这样写:

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

Constructor 类会是什么样子?感谢您的任何想法!

there have been quite some posts about this, all trying to serialize a Func delegate.

But could someone think of an alternative, when the use of the delegate is always clear?

We have a generic create command, which takes a delegate as paramater in the constructor. This delegate will create the Item for the create command:

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();
    }
}

The command is used like this:

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

Then the History serializes the command and sends it to the server. ofc the delegate can't be serialized as is and we get an exception.

Now could someone think of a very simple Constructor class that can be serialized and does the same job than the lambda expresseion? Means it takes a list of paramters and returns an instance of type T, that we then can write somethink like this:

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

How would the Constructor class look like? Thanks for any ideas!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

远山浅 2024-09-17 08:14:48

编辑(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 的具体实现将由您决定。您可以为您需要的每个类创建特定的工厂类,或者您可以创建某种工厂来动态创建实例,例如使用 Activator.CreateInstance 函数或可能使用某种控制反转框架(例如 Spring 或结构图。

下面是使用两个工厂实现的完整示例实现。一种实现可以使用带有匹配参数的类型的构造函数来创建给定参数数组的任何类型。另一个实现创建已在我的“Factory”类中注册的任何类型。

Debug.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
        }
    }
}

编辑(2):重新阅读问题后,我想我更好地了解了提问者真正想要了解的内容。本质上,我们仍然希望利用 lambda 表达式/匿名委托提供的灵活性,但避免序列化问题。

下面是另一个示例实现,它利用 Factory 类来存储用于返回 T 类型实例的委托。

实现 2

[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
        }
    }
}

EDIT(2): I've provided a couple of complete example implementations. They are categorized below as "Implementation 1" and "Implementation 2".

Your delegate is essentially a factory. You could define a factory interface and create a class that implements that interface for your Item class. Below is an example:

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();
    }
}

You would utilize this code in the following manner:

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

EDIT(1): The specific implementations of IFactory<T> that your application needs will be up to you. You could create specific factory classes for each class that you need, or you could create some kind of factory that dynamically creates an instance using, for example, the Activator.CreateInstance function or perhaps using some kind of Inversion of Control framework such as Spring or StructureMap.

Below is a complete example implementation that uses two factory implementations. One implementation can create any type given an array of arguments using that type's constructor with matching parameters. Another implementation creates any type that has been registered with my "Factory" class.

The Debug.Assert statements ensure that everything is behaving as intended. I ran this application without error.

Implementation 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
        }
    }
}

EDIT(2): After re-reading the question, I think I got a better idea of what the asker was really trying to get at. Essentially, we still want to take advantage of the flexibility offered by lambda expressions / anonymous delegates, but avoid the serialization issues.

Below is another example implementation that utilizes a Factory<T> class to store delegates used to return instances of type T.

Implementation 2

[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
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文