How to create a custom control factory method - c#

I have a skinnable Control library that was loads control settings/properties from external xml files.
The Xml classes are in a seperate project as these will be used in a skin editor application, now the question, The controls accept an xml object in the constructor to build the Control but I need to find a nice way to create each control.
Xml class example:
[Serializable]
[XmlInclude(typeof(XmlButton))]
[XmlInclude(typeof(XmlGroup))]
[XmlType(TypeName="Control")]
public class XmlControl
{
[DefaultValue(0)]
public int Width { get; set; }
[DefaultValue(0)]
public int Height { get; set; }
...
and derived types per control type
[Serializable]
[XmlType(TypeName = "Button")]
public class XmlButton : XmlControl
{
public string Label { get; set; }
}
Control classes
public class GUIControl : GUI3DBase
{
public GUIControl(XmlControl skinXml)
{
SkinXml = skinXml;
...
public class GUIButton : GUIControl, IActionControl
{
public GUIButton(XmlControl skinXml) : base(skinXml)
{
}
...
Now this is where I need help, at the moment I have a Method to create controls based on the xml object passed in.
public static GUIControl CreateControl<T>(T skinXml) where T : XmlControl
{
if (skinXml is XmlButton)
{
return new GUIButton(skinXml);
}
else if (skinXml is XmlGroup)
{
return new GUIGroup(skinXml);
}
....
I have about 30 controls and the "if ladder" is growing fast and I feel like I am missing a simple way to create thes controls withou needing to check the xml object type then create the corresponding control type.
I can't add a Type property in the Xml object as that would create circular dependency.
Any help on a good factory method or new stucture layout would be awesome

Maybe IDictionary<Type, Func<XmlControl, GUIControl>> would help. Something like this:
private static Dictionary<Type, Func<XmlControl, GUIControl>> _dictionary = new Dictionary<Type, Func<XmlControl, GUIControl>>()
{
{typeof (XmlControlImpl), x => new GUIControl(x)},
{typeof (XmlGroup), x => new GUIGroup(x)},
};
public static GUIControl CreateControl<T>(T skinXml) where T : XmlControl
{
Func<XmlControl, GUIControl> builder;
if (!_dictionary.TryGetValue(typeof(T), out builder))
throw new KeyNotFoundException("");
return builder(skinXml);
}

Ok, I have found a way to do this with all your ideas and a little reflection, not sure if its the best way but it works nicly and adding a new skinnable control only requires a new xml object and an attribute on the control class.
Attribute Class
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class XmlControlTypeAttribute : Attribute
{
protected Type xmlType;
public XmlControlTypeAttribute(Type xmlType)
{
this.xmlType = xmlType;
}
public Type XmlType
{
get { return this.xmlType; }
}
}
Control:
[XmlControlType(typeof(XmlButton))]
public class GUIButton : GUIControl, IActionControl
{
public GUIButton(XmlControl skinXml) : base(skinXml)
{
}
....
}
Factory method:
public static GUIControl CreateControl2<T>(T skinXml) where T : XmlControl
{
var controlType = Assembly.GetExecutingAssembly().DefinedTypes
.Where(t => t.BaseType == typeof(GUIControl) && t.GetCustomAttribute<XmlControlTypeAttribute>().XmlType.Equals(typeof(T)))
.FirstOrDefault();
return (GUIControl)Activator.CreateInstance(controlType, new[] { skinXml }, null);
}
Thanks for all the ideas the helped heaps, I will leave this question open a bit longer incase somome has a better solution than this.

I would be tempted to add an abstract method to XmlControl:
public abstract class XmlControl
{
[DefaultValue(0)]
public int Width { get; set; }
[DefaultValue(0)]
public int Height { get; set; }
public abstract Type ControlType();
override it in each implementation eg:
public class XmlButton : XmlControl
{
public string Label { get; set; }
public override Type ControlType(){ return typeof(GUIButton); }
}
And then use reflection in the Factory method to construct the right class:
public static GUIControl CreateControl<T>(T skinXml) where T : XmlControl
{
return (GUIControl)Activator.CreateInstance(skinXml.ControlType(),
new[]{skinXml},null);
}

Related

How to serialize a list of generic type which inherit from a non generic class

I have a class structure as follows :
[Serializable]
public abstract class TagConfiguration
{
public abstract string Name { get; set; }
// Other abstract properties
public abstract object GetInitialValue();
public abstract void SetInitialValue(object value);
protected TagConfiguration() { }
// Other methods not useful in the scope of this problem
}
[Serializable]
public class GenericTagConfiguration<T> : TagConfiguration
{
public override string Name { get; set; }
// Other abstract properties
private T _initialValue;
public T InitialValue
{
get => (T)GetInitialValue();
set => SetInitialValue(value);
}
public override object GetInitialValue()
{
return _initialValue;
}
public override void SetInitialValue(object value)
{
_initialValue = (T)value;
}
public GenericTagConfiguration() : base() { }
// Other methods not useful in the scope of this problem
}
This class, as the name implies is for the configuration of a Tag object which looks like this :
[Serializable]
public abstract class Tag
{
public abstract object GetConfiguration();
public abstract void SetConfiguration(object value);
public abstract object GetCurrentValue();
public abstract void SetCurrentValue(object value);
protected Tag() { }
// Some abstract methods
}
[Serializable]
public class GenericTag<T> : Tag
{
private GenericTagConfiguration<T> _configuration;
public TagConfiguration Configuration
{
get => (GenericTagConfiguration<T>)GetConfiguration();
set => SetConfiguration(value);
}
public override object GetConfiguration()
{
return _configuration;
}
public override void SetConfiguration(object value)
{
_configuration = (GenericTagConfiguration<T>)value;
}
private T _currentValue;
public T CurrentValue
{
get => (T)GetCurrentValue();
set => SetCurrentValue(value);
}
public override object GetCurrentValue()
{
return _currentValue;
}
public override void SetCurrentValue(object value)
{
_currentValue = (T)value;
}
public GenericTag() : base() { }
// The same methods from Tag overridden
}
I have to be able to export a List<TagConfiguration> to an XML file but the issue I have is whenever I try to do so, I get an error saying :
Unhandled Exception: System.InvalidOperationException: There was an error generating the XML document.
---> System.InvalidOperationException:The type GenericTagConfiguration[[System.Int32]] was not expected.
Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
I'm using the following code to attempt the export :
List<TagConfiguration> lstTagConf = new List<TagConfiguration>
{
tagConf,
tagConf2
};
XmlSerializer xs = new XmlSerializer(typeof(List<TagConfiguration>));
using (TextWriter tw = new StreamWriter(#"C:\Users\Administrator\Documents\listTag.xml"))
{
xs.Serialize(tw, lstTagConf);
}
QUESTION
The reason I added the Tag class is that I would need to be able to import and export a List<Tag> as well as a List<TagConfiguration>.
Do I need to write an interface to do this ?
The answer I found (Thanks to jdweng and this question) was to add the following before the TagConfiguration class :
[XmlInclude(typeof(GenericTagConfiguration<int>)), ...] // as many different T as you would use
[Serializable]
public abstract class TagConfiguration
{
// Same data
}

Cannot implicitly convert A<B> to A<C<D>> where B inherit from C<D>

I'm building a sort of library to perform text replacement in a document based on some rule. We built a POC and now I'm trying to create a library as generic as possible.
I have just one problem with inheritance:
This is the simplified representation of the classes/interfaces I'm dealing with:
public interface IRule {}
public interface IReplaceRule<T> : IRule
{
T ReplaceValue { get; set; }
}
public class CachedRules<T> where T : IReplaceRule<object>
{
#region Props
public T RuleTemplate { get; set; }
public IDictionary<string, T> RuleList { get; private set; } = null;
#endregion
public void SetRuleList(IDictionary<string, T> ruleList) { ... }
public bool ContainsRuleByKey(string key) { ... }
public bool TryGetRuleValueByKey(string key, out T rule) { ... }
}
public class SingleRowRule : IReplaceRule<string> { ... }
I also have a class which is like a repository of rules, and inside it I can add as many CachedRules as I need:
public class RulesStorage : AbstractRulesStorage
{
private CachedRules<SingleRowRule> singleRowRules;
public RulesStorage() { ... }
// Bunch of methods not useful for this question
// Here I need to return a list of ChachedRule, but just ofr testing I tried to return only one
public CachedRules<IReplaceRule<object>> GetCachedReplaceRules()
{
return singleRowRules;
}
}
Inside this class I need a method to return all the CachedRules declared in the RulesStorage:
Unfortunately the RulesStorage.GetCachedReplaceRules method give me this error:
Cannot implicitly convert type TestLib.Model.CachedRules<TestLib.Rules.SingleRowRule> to TestLib.Model.CachedRules<TestLib.Abstractions.IReplaceRule<object>
I really don't like the fact that I had to put <object> since IReplaceRule requires a generic and also I'm stuck because I don't know how to return this list of CachedRules without getting this compilation error.
Do you have some idea? Do I have to organize the code differently in your opinion?
Hope I've made myself clear and thanks in advance!
Instead of doing IReplaceRule<object> you can do it the way IEnumerable<T> inherits from IEnumerable. With that minor tweak in place, I create an implicit converter to go from T to IReplaceRule and the constraint in place now ensures I can actually do this safely.
I'm assuming you have a reason to have private CachedRules<SingleRowRule> singleRowRules; and can't just using private CachedRules<IReplaceRule> singleRowRules; which would remove the need for this extra conversion hop.
Code:
public interface IReplaceRule : IRule { object ReplaceValue { get; set; } }
public interface IReplaceRule<T> : IReplaceRule { new T ReplaceValue { get; set; } }
public class CachedRules<T> where T : IReplaceRule
{
public IDictionary<string, T> RuleList { get; private set; } = new Dictionary<string, T>();
//The key ingredient for a nice experience instead of just doing this in the method
public static implicit operator CachedRules<IReplaceRule>(CachedRules<T> rules)
=> new CachedRules<IReplaceRule> { RuleList = rules.RuleList.ToDictionary(x => x.Key, x => x.Value as IReplaceRule) };
}
public class SingleRowRule : IReplaceRule<string>
{
public string ReplaceValue { get; set; }
object IReplaceRule.ReplaceValue { get => ReplaceValue; set => ReplaceValue = value as string; }
}
public class RulesStorage
{
private CachedRules<SingleRowRule> singleRowRules = new CachedRules<UserQuery.SingleRowRule>();
//FIXME: just for testing purposes
public RulesStorage() => singleRowRules.RuleList.Add("Hello", new SingleRowRule { ReplaceValue = "World" });
// Here I need to return a list of ChachedRule, but just ofr testing I tried to return only one
public CachedRules<IReplaceRule> GetCachedReplaceRules() => singleRowRules;
}

C# Is Not Assignable Type - Generics

So I'm just hacking around with a state machine type I was working on and mostly wanting to just try out the Activator.CreateInstance method to see what it was like, and I ran into a problem where I cant seem to use the where clause as I would think. I apologize ahead of time if I am just an idiot and everyone laughs me out of here. So I have 2 small classes.
public class TransitionContainer<TTransition, TStateTo> :
ITransitionContainer<TTransition, TStateTo>
where TTransition : ITransition
where TStateTo : IState
{
public TransitionContainer()
{
StateTo = typeof(TStateTo);
Transition = Activator.CreateInstance<TTransition>();
}
public Type StateTo { get; private set; }
public TTransition Transition { get; private set; }
}
as well as
public class StateContainer<T> : IStateContainer<T> where T : IState
{
private Dictionary<Type, TransitionContainer<ITransition, IState>> _transitions =
new Dictionary<Type, TransitionContainer<ITransition, IState>>();
public StateContainer()
{
State = Activator.CreateInstance<T>();
}
public T State { get; private set; }
public int TransitionCount
{
get { return _transitions.Count; }
}
public void AddTransition<TTransition, TStateTo>() where TTransition : ITransition, new()
where TStateTo : IState, new()
{
var transitionContainer= new TransitionContainer<TTransition, TStateTo>();
_transitions.Add(typeof(TTransition), transitionContainer);
}
So on the line _transitions.Add(typeof(TTransition), transitionContainer); I receive a cannot convert TransitionContainer<TTransition,TStateTo> expression to type TransitionContainer<ITransition,IState> error.
If I change the generic parameters to
var transitionContainer= new TransitionContainer<ITransition, IState>();
it works fine, but I wanted to use inherited types that are new() so I could be sure I could instantiate them.
Again I apologize if I'm doing something incredibly wrong, I was just kind of ran into a brick wall and my googling led me in no good direction. I didnt include any of the other interfaces or classes as they didn't seem to be part of the problem, but if there needed I can attach them. Thanks for any help!
This issue happens because:
ITransitionContainer is not a covariant interface over its type arguments.
AddTransition method generic arguments are not constrained to be reference types.
_transitions is not a dictionary with ITransitionContainer values, so without changing it to Dictionary<Type, ITransitionContainer<ITransition, IState>> we still won't be able to add even properly resticted covariant transtions.
Simplified example
Consider the following simplified case:
public interface ITransition
{
}
public class SomeTransition : ITransition
{
}
public interface ITest<TTransition>
where TTransition : ITransition
{
TTransition Value { get; }
}
public class SomeTest<TTransition> : ITest<TTransition>
where TTransition : ITransition
{
public TTransition Value
{
get
{
throw new NotImplementedException();
}
}
}
It will fail in both
public static void Do<TTransition>()
where TTransition : ITransition
{
ITest<ITransition> item = new SomeTest<TTransition>();
}
and
ITest<ITransition> item = new SomeTest<SomeTransition>();
If you make ITest covariant
public interface ITest<out TTransition>
, then it will fail only in generic method. Because here TTransition can be a struct and co/(contra)variance doesn't work with value types:
public static void Do<TTransition>()
where TTransition : ITransition
{
ITest<ITransition> item = new SomeTest<TTransition>();
}
But if you make that method constrained to only reference types, then it will work in both cases:
public static void Do<TTransition>()
where TTransition : class, ITransition
{
ITest<ITransition> item = new SomeTest<TTransition>();
}
Apply the same principle(out and class) to your two generic arguments and it will do the job.
Full solution for your specific case:
public interface IState
{ }
public interface ITransition
{ }
// !!!!! - Here we add out specifier
public interface ITransitionContainer<out TTransition, out TStateTo>
where TTransition : ITransition
where TStateTo : IState
{
Type StateTo
{
get;
}
TTransition Transition
{
get;
}
}
public interface IStateContainer<T> where T : IState
{
T State
{
get;
}
}
public class TransitionContainer<TTransition, TStateTo> : ITransitionContainer<TTransition, TStateTo>
where TTransition : ITransition
where TStateTo : IState
{
public TransitionContainer()
{
StateTo = typeof(TStateTo);
Transition = Activator.CreateInstance<TTransition>();
}
public Type StateTo { get; private set; }
public TTransition Transition { get; private set; }
}
public class StateContainer<T> : IStateContainer<T> where T : IState
{
private Dictionary<Type, ITransitionContainer<ITransition, IState>> _transitions =
new Dictionary<Type, ITransitionContainer<ITransition, IState>>();
public StateContainer()
{
State = Activator.CreateInstance<T>();
}
public T State { get; private set; }
public int TransitionCount
{
get { return _transitions.Count; }
}
public void AddTransition<TTransition, TStateTo>()
// !!!!!! - Here we add class constraints
where TTransition : class, ITransition, new()
where TStateTo : class, IState, new()
{
var transitionContainer = new TransitionContainer<TTransition, TStateTo>();
_transitions.Add(typeof(TTransition), transitionContainer);
}
}
That fails because generics are not covariant. The problem can be seen here:
TransitionContainer<ITransition, IState> value = new TransitionContainer<TTransition, TStateTo>();
That gives you the same error. You also get this error with something as simple as:
List<IComparable> list = new List<DateTime>();
Visual Studio tells you (basically) that:
Cannot implicitly convert type 'List<System.DateTime>' to 'List<System.IComparable>'
What you need to do is convert the object. You could create a Convert method that returns a TransitionContainer<ITransition, IState> and then use .Add(typeof(TTransition), transitionContainer.Convert()) (or whatever you name it).
But the most painless option is to create an implicit conversion for your TransitionContainer<TTransition, TStateTo> object by adding this static method:
public static implicit operator TransitionContainer<ITransition, IState>(TransitionContainer<TTransition, TStateTo> value)
{
return new TransitionContainer<ITransition, IState>() { StateTo = value.StateTo, Transition = value.Transition };
}
And that's it. :)
Of course, you will have to copy everything needed for it to work, in this case it seems these two objects are enough.

C# Object Inheritance

I am trying to create a base class in c# that I can extend out to sub classes.
For example:
public class ObjectsInTheSky
{
public string Size, Shape;
public float Mass;
public int DistanceFromEarth;
public bool hasAtmosphere, hasLife;
public enum ObjectTypes {Planets,Stars,Moons}
public ObjectsInTheSky( int id )
{
this.Load( id );
}
public void Load( int id)
{
DataTable table = Get.DataTable.From.DataBase(id);
System.Reflection.PropertyInfo[] propInfo = this.GetType().GetProperties();
Type tp = this.GetType();
foreach (System.Reflection.PropertyInfo info in propInfo)
{
PropertyInfo p = tp.GetProperty(info.Name);
try
{
if (info.PropertyType.Name == "String")
{
p.SetValue(this, table.Rows[0][info.Name].ToString(), null);
}
else if (info.PropertyType.Name == "DateTime")
{
p.SetValue(this, (DateTime)table.Rows[0][info.Name], null);
}
else
{
p.SetValue(this, Convert.ToInt32(table.Rows[0][info.Name]), null);
}
}
catch (Exception e)
{
Console.Write(e.ToString());
}
}
}
}
public class Planets : ObjectsInTheSky
{
public Moons[] moons;
}
public class Moons : ObjectsInTheSky
{
}
public class Stars : ObjectsInTheSky
{
public StarTypes type;
public enum StarTypes {Binary,Pulsar,RedGiant}
}
My problem is when I try to use an object:
Stars star = new Stars(142);
star.type does not exists and property of star, it exists as star.star.type but completely inaccessable, or I can not figure out how to access it.
I do not know if I'm extending the ObjectsInTheSky property properly or not. Any help or pointers will be greatly appreciated.
It looks as though you are trying to use a constructor that is not defined on your subclass Stars or the base class.
Stars star = new Stars(142);
If you are trying to use the .Load(int) method then you would need to do this:
Stars star = new Stars();
star.Load(142);
Or, if you are trying to use the base constructor, you need to define it in the subclass:
public class Stars : ObjectsInTheSky
{
public Stars(int id) : base(id) // base class's constructor passing in the id value
{
}
public Stars() // in order to not break the code above
{
}
public StarTypes type;
public enum StarTypes {Binary,Pulsar,RedGiant}
}
Constructors in C# are not inherited. You need to add the additional constructor overloads to each of the base classes:
public class Stars : ObjectsInTheSky
{
public Stars(int id) : base(id) { }
public StarTypes type;
public enum StarTypes {Binary,Pulsar,RedGiant}
}
This will create a constructor that just calls the base class's constructor for you.

Can I somehow tidy up this (overuse?) of generics?

I'm building a generic flat file reader which looks something like this.
public class GenericReader<TComposite, THeader, TData, TTrailer>
where TComposite : GenericComposite<THeader, TData, TTrailer>, new()
where THeader : new()
where TData : new()
where TTrailer : new()
{
public TComposite Read()
{
var composite = new TComposite();
composite.Header = new THeader();
composite.Data = new TData();
composite.Trailer = new TTrailer();
return composite;
}
}
It could be consumed like so.
var reader = new GenericReader<Composite<Header, Data, Trailer>, Header, Data, Trailer> ();
var composite = reader.Read();
Console.WriteLine(composite.Data.SomeProperty);
Console.ReadLine();
Here are the classes used.
public class Composite<THeader, TData, TTrailer> : GenericComposite<THeader, TData, TTrailer>
{
}
public class GenericComposite<THeader, TData, TTrailer>
{
public THeader Header { get; set; }
public TData Data { get; set; }
public TTrailer Trailer { get; set; }
}
public class Header {
public string SomeProperty { get { return "SomeProperty"; } }
}
public class Data {
public string SomeProperty { get { return "SomeProperty"; } }
}
public class Trailer {
public string SomeProperty { get { return "SomeProperty"; } }
}
Is there a way how I could remove or encapsulate that generic type information in the GenericReader? I'm looking for an extra pair of eyes to show me something what I've been missing. We already did something with returning interfaces, and making the consumer do a cast, but that just moves the responsibility to the wrong location in my opinion, plus there is a small performance penalty.
Thanks.
Edit: I don't need the TComposite, I can just return the GenericComposite. How could I miss that?
public class GenericReader<THeader, TData, TTrailer>
where THeader : new()
where TData : new()
where TTrailer : new()
{
public GenericComposite<THeader, TData, TTrailer> Read()
{
var composite = new GenericComposite<THeader, TData, TTrailer>();
composite.Header = new THeader();
composite.Data = new TData();
composite.Trailer = new TTrailer();
return composite;
}
}
public class GenericComposite<THeader, TData, TTrailer>
{
public THeader Header { get; set; }
public TData Data { get; set; }
public TTrailer Trailer { get; set; }
}
There's no way to remove the need for the type declarations on the generic constraints that you have.
However, your use case suggests that this is the most common behavior:
var reader = new GenericReader<Composite<Header, Data, Trailer>,
Header, Data, Trailer>();
If this is the case, where you can make assumptions about the frequency with which certain patterns are used, you can inherit a type (or set of types) from the generic classes with closed type definitions which can be used more easily.
In the above case, you can provide these classes for the base, most common cases (in addition to the generic definitions):
public class Composite : GenericComposite<Header, Data, Trailer> { }
public class GenericReader : GenericReader<
Composite, Header, Data, Trailer>
{ }
Which would then be used like so:
var reader = new GenericReader();
var composite = reader.Read();
Console.WriteLine(composite.Data.SomeProperty);
Console.ReadLine();
You'll still have the types with the generic parameters to use for highly-specialized cases, but for common use cases (which you determine through analysis/domain knowledge), you can determine what the most common one is and provide classes with set type parameters to assist.

Categories