gpt4 book ai didi

c# - 创建可序列化匿名委托(delegate)的替代方案

转载 作者:太空宇宙 更新时间:2023-11-03 11:46:30 40 4
gpt4 key购买 nike

已经有很多关于这个的帖子,都试图序列化一个 Func 委托(delegate)。

但是当委托(delegate)的使用总是很明确时,有人能想到一个替代方案吗?

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

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

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

现在有人能想出一个非常简单的 Constructor 类,它可以序列化并完成与 lambda 表达式相同的工作吗?意味着它需要一个参数列表并返回一个类型 T 的实例,然后我们可以这样写:

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

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

最佳答案

编辑(2):我提供了几个完整的示例实现。它们在下面分类为“实现 1”和“实现 2”。

您的委托(delegate)本质上是一个工厂。您可以定义一个工厂接口(interface)并创建一个类来为您的 Item 类实现该接口(interface)。下面是一个例子:

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 或结构图。

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

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 表达式/匿名委托(delegate)提供的灵 active ,但要避免序列化问题。

下面是另一个使用 Factory<T> 的示例实现存储用于返回类型 T 实例的委托(delegate)的类。

实现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
}
}
}

关于c# - 创建可序列化匿名委托(delegate)的替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3292191/

40 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com