I am trying to refactor a piece of code which seems easily refactorable but is proving difficult. There are two method which seem very similar and I feel should be refactored:-
public class MyClass
{
private void AddBasicData(Receiver receiver)
{
var aHelper = new AHelper();
var bHelper = new BHelper();
var cHelper = new CHelper();
receiver.ObjA = aHelper.GetBasic();
receiver.ObjB = bHelper.GetBasic();
receiver.ObjC = cHelper.GetBasic();
}
private void AddExistingData(Receiver receiver)
{
var aHelper = new AHelper();
var bHelper = new BHelper();
var cHelper = new CHelper();
receiver.ObjA = aHelper.GetExisting();
receiver.ObjB = bHelper.GetExisting();
receiver.ObjC = cHelper.GetExisting();
}
}
The reference code for this class is here...
public class AHelper : Helper<A>
{
}
public class BHelper : Helper<B>
{
}
public class CHelper : Helper<C>
{
}
public class Helper<T> : IHelper<T> where T : IMyObj
{
public T GetBasic()
{
...
}
public T GetExisting()
{
...
}
}
public interface IHelper<T>
{
T GetBasic();
T GetExisting();
}
public class A : IMyObj {}
public class B : IMyObj {}
public class C : IMyObj {}
public interface IMyObj {}
public class Receiver
{
public A ObjA { get; set; }
public B ObjB { get; set; }
public C ObjC { get; set; }
}
My first attempt was to refactor like this...
public class MyClass
{
private void AddBasicData(Receiver receiver)
{
Func<Helper<IMyObj>, IMyObj> func = x => x.GetBasic();
AddData(receiver, func);
}
private void AddExistingData(Receiver receiver)
{
Func<Helper<IMyObj>, IMyObj> func = x => x.GetExisting();
AddData(receiver, func);
}
private void AddData(Receiver receiver, Func<Helper<IMyObj>, IMyObj> func)
{
var aHelper = new AHelper();
var bHelper = new BHelper();
var cHelper = new CHelper();
receiver.ObjA = func(aHelper);
receiver.ObjB = func(bHelper);
receiver.ObjC = func(cHelper);
}
}
The problem with this is objects like new AHelper() is not assignable to Helper<IMyObj> :-(
Can anyone see how this could be nicely refactored?
Thanks in advance
Russell
Try using a templated function. It should infer the type based on the type of parameter you pass, so you shouldn't need to explicitly specify the type in the AddData call.
public class MyClass
{
private void AddData<T>(Receiver receiver, Func<Helper<T>, T> func)
{
var aHelper = new AHelper();
var bHelper = new BHelper();
var cHelper = new CHelper();
receiver.ObjA = func(aHelper);
receiver.ObjB = func(bHelper);
receiver.ObjC = func(cHelper);
}
}
Attempt #2:
Tricky problem
I think you need a more generic IHelper interface. Would something like this help?
public interface IHelper
{
IMyObj GetBasic();
IMyObj GetExisting();
}
public interface IHelper<T> : IHelper
{
T GetBasic();
T GetExisting();
}
You'll have to work out the name conflict between the derived interface and the base interface, but I'm not sure exactly how you'd want to do that, and I'm running out of time, so I'll leave that as it for the moment.
Attempt #3 (I'm determined to get this!): Would this be cheating?
public enum RetrievalMethod
{
Basic,
Existing
}
public class Helper<T> : IHelper<T> where T : IMyObj
{
public T Get(RetrievalMethod rm)
{
switch(rm)
{
case RetrievalMethod.Basic:
return GetBasic();
case RetrievalMethod.Existing:
return GetExisting();
}
}
...
}
...
private void AddData(Receiver receiver, RetrievalMethod rm)
{
var aHelper = new AHelper();
var bHelper = new BHelper();
var cHelper = new CHelper();
receiver.ObjA = aHelper.Get(rm);
receiver.ObjB = bHelper.Get(rm);
receiver.ObjC = cHelper.Get(rm);
}
You can use casting for solving assigning problem. If AHelper actually return A, I think this works
private void AddData(Receiver receiver, Func<Helper<IMyObj>, IMyObj> func)
{
var aHelper = new AHelper();
var bHelper = new BHelper();
var cHelper = new CHelper();
receiver.ObjA = (A) func(aHelper);
receiver.ObjB = (B) func(bHelper);
receiver.ObjC = (C) func(cHelper);
}
if you override methods, you can do casting, dont need to change definition of "Func, IMyObj>"
public class AHelper : Helper<A>
{
public override A GetBasic()
{
return new A();
}
}
What about this?
private static T GetData<T, THelper>(Func<THelper, T> func)
where THelper : Helper<T>, new()
where T : IMyObj
{ return func(new THelper()); }
private static T GetBasicData<T, THelper>()
where THelper : Helper<T>, new()
where T : IMyObj
{ return GetData(x => x.GetBasic()); }
private static T GetExistingData<T, THelper>()
where THelper : Helper<T>, new()
where T : IMyObj
{ return GetData(x => x.GetExisting()); }
private void AddBasicData(Receiver receiver)
{
receiver.ObjA = GetBasicData<A, AHelper>();
receiver.ObjB = GetBasicData<B, BHelper>();
receiver.ObjC = GetBasicData<C, CHelper>();
}
private void AddExistingData(Receiver receiver)
{
receiver.ObjA = GetExistingData<A, AHelper>();
receiver.ObjB = GetExistingData<B, BHelper>();
receiver.ObjC = GetExistingData<C, CHelper>();
}
Related
I have a class that has an ICollection property that is assigned by the constructor when the class is instantiated, but I want to bind this property to the original collection so that when it's updated/changed, the original list is as well. What is the best method of doing this?
Here's an example:
public class Organizations
{
private ICollection<Organization> _orgs;
public Organizations(ICollection<Organization> orgs)
{
_orgs = orgs;
}
public void TestAdd()
{
_orgs.Add(new Organization {Name = "Testing 123"});
}
}
// in another class
public ActionResult TestApi()
{
var tmp = new SyncTool.Core.Extensions.Zendesk.Organizations(ZendeskCache.Organizations.Data);
var zd = ZendeskCache.Organizations.Data.FirstOrDefault(n => n.Name.Contains("Testing 123"));
//ZendeskCache.Org.... is a List<Organization>
return Json(new {data = "tmp" }, AG);
}
The List<Organization> you are passing to the constructor is a reference object. This code works the way you want it to (aside from syntax errors), have you tried it out?
To reproduce more simply:
public class Program
{
public static void Main(string[] args)
{
var orgs = new List<string>();
var orgClass = new Organizations(orgs);
orgClass.TestAdd();
Console.WriteLine(orgs.First());
Console.Read();
}
}
public class Organizations
{
private ICollection<string> _orgs;
public Organizations(ICollection<string> orgs)
{
_orgs = orgs;
}
public void TestAdd()
{
_orgs.Add("Testing 123");
}
}
//Output: "Testing 123"
I'm using fluentvalidation and lightinject
Here is my code to insert a blog article;
public OperationResultDto Add(BlogArticleDto blogArticleDto)
{
OperationResultDto result = new OperationResultDto();
ValidationResult validationResult =
_blogArticleModelValidator.Validate(blogArticleDto);
if (!validationResult.IsValid)
{
result.IsOk = false;
ValidationFailure firstValidationFailer =
validationResult.Errors.FirstOrDefault();
if (firstValidationFailer != null)
{
result.Message = firstValidationFailer.ErrorMessage;
}
return result;
}
BlogArticle blogArticle = new BlogArticle {
Title = blogArticleDto.Title,
ShortBody = blogArticleDto.ShortBody,
Body = blogArticleDto.Body,
IsOnline = blogArticleDto.IsOnline,
CategoryName = blogArticleDto.CategoryName,
PublishedBy = blogArticleDto.PublishedBy,
PublishDate = blogArticleDto.PublishDate,
Tags = new List<string>(), //TODO parse, model's tags in one string.
CreateDate = DateTime.Now,
MainPhotoPath = blogArticleDto.MainPhotoPath,
};
_blogArticleRepository.Add(blogArticle);
return result;
}
As you can see, "validation section" is huge and I don't want to validate my dto parameters in my service(business) layer. I want to validate "arguments" in my ioc (lightinject).
Here is my ioc code to proceed that;
public class ServiceInterceptor : IInterceptor
{
public object Invoke(IInvocationInfo invocationInfo)
{
Log.Instance.Debug("Class: ServiceInterceptor -> Method: Invoke started.");
string reflectedTypeFullname = String.Empty;
string methodName = String.Empty;
if (invocationInfo.Arguments.Any())
{
//TODO Validate method parameters here..
foreach (object argument in invocationInfo.Arguments)
{
}
}
if (invocationInfo.Method.ReflectedType != null)
{
reflectedTypeFullname = invocationInfo.Method.ReflectedType.FullName;
methodName = invocationInfo.Method.Name;
}
... ...
Now, I can take all arguments of a method to give them to my fluentvalidator. So I know I need to define typeOf argument here but after that how can I call fluent validation's related validation object* to validate argument ?
I am the author of LightInject and maybe you could see if this example works out for you.
class Program
{
static void Main(string[] args)
{
var container = new ServiceContainer();
container.Register<AbstractValidator<Foo>, FooValidator>();
container.Register<IFooService, FooService>();
container.Intercept(sr => sr.ServiceType.Name.EndsWith("Service"), factory => new ServiceInterceptior(factory));
var service = container.GetInstance<IFooService>();
service.Add(new Foo());
}
}
public interface IFooService
{
void Add(Foo foo);
}
public class FooService : IFooService
{
public void Add(Foo foo)
{
}
}
public class Foo
{
}
public class FooValidator : AbstractValidator<Foo>
{
}
public class ServiceInterceptior : IInterceptor
{
private readonly IServiceFactory factory;
public ServiceInterceptior(IServiceFactory factory)
{
this.factory = factory;
}
public object Invoke(IInvocationInfo invocationInfo)
{
foreach (var argument in invocationInfo.Arguments)
{
Type argumentType = argument.GetType();
Type validatorType = typeof (AbstractValidator<>).MakeGenericType(argumentType);
var validator = factory.TryGetInstance(validatorType);
if (validator != null)
{
var validateMethod = validatorType.GetMethod("Validate", new Type[] { argumentType });
var result = (ValidationResult)validateMethod.Invoke(validator, new object[] { argument });
if (!result.IsValid)
{
//Throw an exception, log or any other action
}
}
}
//if ok, proceed to the actual service.
return invocationInfo.Proceed();
}
}
I have a Command class that, when executed, performs a command on a IScriptable object
[
XmlInclude(typeof(CommandPositionSettings)),
]
public abstract class CommandSettings
{
public List<Command> Parameters = new List<Command>(4);
public abstract Command Load();
}
public abstract class Command
{
public List<Command> Parameters = new List<Command>(4);
public Command(CommandSettings settings)
{
Parameters = settings.Parameters;
}
public abstract void Execute(IScriptable scriptableObject);
public abstract int GetArgumentCount();
public abstract CommandSettings Write();
}
This works well for certain commands such as Position
public class CommandPosition : Command
{
public CommandPosition(CommandPositionSettings settings)
:base(settings = settings == null ? new CommandPositionSettings() : settings)
{
if (settings == null)
{
settings = new CommandPositionSettings();
}
}
public override void Execute(IScriptable scriptableObject)
{
Vector3 position = new Vector3();
position.X = Parameters[0].Execute();
position.Y = Parameters[1].Execute();
position.Z = Parameters[2].Execute();
scriptableObject.Position = position;
}
public override int GetArgumentCount()
{
return 3;
}
public override CommandSettings Write()
{
CommandPositionSettings settings = new CommandPositionSettings();
settings.Parameters.AddRange(Parameters);
return settings;
}
}
until I try to execute a different type of command that only needs to return a value instead of requiring an IScriptable. In the above code this would be where the X, Y and Z values are obtained for the position.
At this point it would be fine to store each parameter as a float but sometimes I wish to do something a bit more involved than return a value. For example randomise between two values each time the Command is executed.
public class CommandRandom : Command
{
public CommandRandom(CommandRandomSettings settings)
: base(settings = settings == null ? new CommandRandomSettings() : settings)
{
if (settings == null)
{
settings = new CommandRandomSettings();
}
}
public override float Execute()
{
return ToolBox.Random.NextFloat(Parameters[0], Parameters[1]);
}
public override int GetArgumentCount()
{
return 2;
}
public override CommandSettings Write()
{
CommandPositionSettings settings = new CommandPositionSettings();
settings.Parameters.AddRange(Parameters);
return settings;
}
}
This command is only required to return a random floating point number and not directly affect the IScriptable object.
Another reason why I am currently deriving them from the same class is that when a command line string is interpreted it creates a new Command for each matching form of Command(para0, para1...paraN).
A Command can also be nested inside another Command.
So Command(para0, para1...paraN) where: para1 = Command(para0, para1...paraN)
How then can I distinguish between a nested Command that is only required to return a value and one that directly affects the IScriptable object itself?
You could solve it using generic types like:
public interface IScriptable
{
Vector3 Position;
}
public abstract class CommandSettings<T>
{
public List<Command<T>> Parameters = new List<Command<T>>(4);
public abstract Command<T> Load();
}
public class CommandRandomSettings : CommandSettings<float>
{
public Command<float> Load()
{
return null;
}
}
public class CommandPositionSettings : CommandSettings<object>
{
public Command<object> Load()
{
return null;
}
}
public abstract class Command<T>
{
public List<Command<T>> Parameters = new List<Command<T>>(4);
public Command(CommandSettings<T> settings)
{
Parameters = settings.Parameters;
}
public abstract T Execute(IScriptable scriptableObject);
public abstract int GetArgumentCount();
public abstract CommandSettings<T> Write();
}
public class CommandRandom : Command<float>
{
public CommandRandom(CommandRandomSettings settings)
: base(settings = settings == null ? new CommandRandomSettings() : settings)
{
if (settings == null)
{
settings = new CommandRandomSettings();
}
}
public override float Execute()
{
return 0.0f;
}
public override int GetArgumentCount()
{
return 2;
}
public override CommandSettings<float> Write()
{
CommandRandomSettings settings = new CommandRandomSettings();
settings.Parameters.AddRange(Parameters);
return settings;
}
}
public class CommandPosition : Command<object>
{
public CommandPosition(CommandPositionSettings settings)
: base(settings = settings == null ? new CommandPositionSettings() : settings)
{
if (settings == null)
{
settings = new CommandPositionSettings();
}
}
public override object Execute(IScriptable scriptableObject)
{
Vector3 position = new Vector3();
position.X = Parameters[0].Execute();
position.Y = Parameters[1].Execute();
position.Z = Parameters[2].Execute();
scriptableObject.Position = position;
}
public override int GetArgumentCount()
{
return 3;
}
public override CommandSettings<object> Write()
{
CommandPositionSettings settings = new CommandPositionSettings();
settings.Parameters.AddRange(Parameters);
return settings;
}
}
Unfortunately you cannot pass void as the type parameter in this case. In the above sample I used object instead. The Execute(IScriptable scriptableObject) method could simply return null.
This code:
public interface IInter { }
public class Concrete : IInter { /*... body ...*/ }
var t = (List<IInter>)new List<Concrete>();
Yields this error:
Cannot convert type 'System.Collections.Generic.List<Concrete>' to 'System.Collections.Generic.List<IInter>'
Why is it ? how do I overcome it ? my goal is this:
var t = new List<List<IInter>>()
{
new List<ConcreteA>(){/* ... data ... */},
new List<ConcreteB>(){/* ... data ... */},
// ...
new List<ConcreteX>(){/* ... data ... */},
};
Edit:
Thanks for all your help. Ahh, I kinda abstracted things, to make it easier to read... but my real problem is this:
public class SingletonFactory<T> where T : IToken
{
private SingletonFactory() { }
private static SingletonFactory<T> _instance = new SingletonFactory<T>();
public static SingletonFactory<T> Instance { get { return _instance; } }
public T Produce(int position) { return (T)Activator.CreateInstance(typeof(T), position); }
public T Produce(int position, string token) { return (T)Activator.CreateInstance(typeof(T), position, token); }
}
And then:
var keywords = new Dictionary<string, SingletonFactory<IToken>>()
{
{ "abc", SingletonFactory<Abc>.Instance },
{ "xyz", SingletonFactory<Xyz>.Instance },
{ "123", SingletonFactory<Num>.Instance }
};
So I guess it's much more complicated...
I'm using c# 4.0
var res = new List<Concrete>().Cast<IInter>().ToList();
A List<Concrete> is not a List<IInter> although Concrete implements IInter.
You can use:
List<IInter> t = ConcreteList.Cast<IInter>().ToList();
Covariance and Contravariance FAQ
Because you cannot assign a List<ConcreteA> to a List<IInter>, otherwise you could do this:
concreteAList = newList<ConcreteA>();
List<Inter> interList = concreteAList as List<Inter>; // seems harmless
interList.Add(new ConcreteB()); // not allowable
You could do:
var t = new List<List<IInter>>()
{
new List<IInter>(){/* ... fill with ConcreteAs ... */},
new List<IInter>(){/* ... fill with ConcreteBs ... */},
// ...
new List<IInter>(){/* ... fill with ConcreteXs ... */},
};
But I don't know if that accomplishes what you want.
This is called covariance and C# supports covariance (and contravariance) on generic interfaces and not generic classes. This is the following works but your example does not:
IEnumerable<IInter> e = new List<Concrete>();
ICollection<IInter> c = new List<Concrete>();
Covariance is also supported on arrays:
IInter[] a = new Concrete[3];
Make a covrariant interface for the methods you need to access from SingletonFactory<T>. This will ensure that your type T is output type safe.
public interface IToken { }
public class Abc : IToken { }
public class Xyz : IToken { }
public class Num : IToken { }
public interface ISingletonFactory<out T> where T : IToken
{
T Produce(int position);
T Produce(int position, string token);
}
public class SingletonFactory<T> : ISingletonFactory<T> where T : IToken
{
private SingletonFactory() { }
private static SingletonFactory<T> _instance = new SingletonFactory<T>();
public static SingletonFactory<T> Instance { get { return _instance; } }
public T Produce(int position) { return (T)Activator.CreateInstance(typeof(T), position); }
public T Produce(int position, string token) { return (T)Activator.CreateInstance(typeof(T), position, token); }
}
var keywords = new Dictionary<string, ISingletonFactory<IToken>>()
{
{ "abc", SingletonFactory<Abc>.Instance },
{ "xyz", SingletonFactory<Xyz>.Instance },
{ "123", SingletonFactory<Num>.Instance }
};
One way is:
IEnumerable<IInter> t = new List<Concrete>();
Another is:
List<IInter> t = new List<Concrete>().ConvertAll(x => (IInter) x);
Edit, adding better sample to show that it works (the test passes):
[Test]
public void CovarianceTest()
{
var concrete = new Concrete();
IEnumerable<IInter> t = new List<Concrete> { concrete };
Assert.IsTrue(new[] { concrete }.SequenceEqual(t));
List<IInter> t2 = new List<Concrete> { concrete }.ConvertAll(x => (IInter)x);
Assert.IsTrue(new[] { concrete }.SequenceEqual(t2));
}
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!
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
}
}
}