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

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.

Related

Class with generic parameter of the same generic class type

I had a class which represents a prefix search tree:
public class PrefixTree<TData>
{
private PrefixTree<TData>[] _children;
private void SomeMethod()
{
_children = new PrefixTree<TData>[10];
}
}
Then I created a derived class with additional features for its nodes:
public class NewPrefixTree<TData> : PrefixTree<TData>
The problem is that in SomeMethod() of derived class we still create instances of base class and it doesn't fit the meaning.
I refactored the base class to this:
public abstract class PrefixTree<TData, TNode>
where TNode : PrefixTree<TData, TNode>, new()
{
private TNode[] _children;
private void SomeMethod()
{
_children = new TNode[10];
}
}
Despite the base class has complete functionality itself, I had to make it abstract because I can't write new DigitalPrefixTree<TData, DigitalPrefixTree<int, ...>>() .
But now I can use it this way and it works perfectly:
public class NewPrefixTree<TData> : PrefixTree<TData, NewPrefixTree<TData>> {} // for derived class
//or
public class PrefixTree<TData> : PrefixTree<TData, PrefixTree<TData>> {} // to use the base functionality
I've never done this before and I wonder if it's a good idea to declare a class with generic parameter of the same generic class type. Or I need to make some tricks with co/contra-variance of generic interfaces (but it probably doesn't work as I use the class type as method’s parameters type and as return type as well)?
Try this:
public interface INode<out TData>
{
TData Data { get; }
IEnumerable<INode<TData>> Children { get; }
public IEnumerable<TRequiredData> GetNestedData<TRequiredData>();
}
public interface ITree<out TData>
{
IEnumerable<INode<TData>> Children { get; }
IEnumerable<TRequiredData> GetNestedData<TRequiredData>();
}
public class Node<TData> : INode<TData>
{
public TData Data { get; }
public IEnumerable<INode<TData>> Children { get; }
public Node(TData data, IEnumerable<INode<TData>>? children = null)
{
Data = data;
Children = children ?? Enumerable.Empty<INode<TData>>();
}
public IEnumerable<TRequiredData> GetNestedData<TRequiredData>()
{
List<TRequiredData> result = new();
if (Data is TRequiredData requiredData)
result.Add(requiredData);
foreach (var child in Children)
result.AddRange(child.GetNestedData<TRequiredData>());
return result;
}
}
public class Tree<TData> : ITree<TData>
{
public IEnumerable<INode<TData>> Children { get; }
public Tree(IEnumerable<INode<TData>> children)
{
Children = children;
}
public IEnumerable<TRequiredData> GetNestedData<TRequiredData>()
{
List<TRequiredData> result = new();
foreach (var node in Children)
result.AddRange(node.GetNestedData<TRequiredData>());
return result;
}
}
And here is example of usage:
record SomeRecord();
class SomeClass {}
static void Main(string[] args)
{
var nodeWithNested = new Node<SomeClass>(
data: new SomeClass(),
children: new List<INode<SomeClass>>()
{
new Node<SomeClass>(new SomeClass()),
new Node<SomeClass>(new SomeClass())
});
var nodes = new List<INode<object>>()
{
new Node<SomeClass>(new SomeClass()),
nodeWithNested,
new Node<SomeRecord>(new SomeRecord()),
};
var tree = new Tree<object>(nodes);
var someClasses = tree.GetNestedData<SomeClass>(); // 4 items
var someRecords = tree.GetNestedData<SomeRecord>(); // 1 item
}
This approach based on out generic modifier.
The only restriction is that you can not use structs (int, bool and ect.) as they don't have common object to cast to.
Hope this will be useful.

How to return interface using generic in C#

I am new in C# Generic concept and I would like to return interface implemented class using generic concept. Below is my example which is currently implemented without generic:
1) Factory Class which return interface and this class has two overload method which accept different data model:
public class Factory
{
public ICommon Init(DBInfoData dbInfoData)
{
return new ClassA(dbInfoData);
}
public ICommon Init(WebInfoData webInfoData)
{
return new ClassB(webInfoData);
}
}
2) Interface and interface implemented two class as below:
//=== Common Interface
public interface ICommon
{
void MethodA();
void MethodB();
}
//=== Internal access only ClassA
internal class ClassA : ICommon
{
private DBInfoData _DBInfoData = null;
public ClassA(DBInfoData dbInfoData)
{
_DBInfoData = dbInfoData;
}
public void MethodA()
{
throw new NotImplementedException();
}
public void MethodB()
{
throw new NotImplementedException();
}
}
//=== Internal access only ClassB
internal class ClassB : ICommon
{
private WebInfoData _WebInfoData = null;
public ClassB(WebInfoData webInfoData)
{
_WebInfoData = webInfoData;
}
public void MethodA()
{
throw new NotImplementedException();
}
public void MethodB()
{
throw new NotImplementedException();
}
}
3) Data Model class as below:
//=== Database Information
public class DBInfoData
{
public string Server { get; set; }
public string Database { get; set; }
}
//=== Web Server Information
public class WebInfoData
{
public string URL { get; set; }
public int Port { get; set; }
}
Now I want to implement generic functionality of C# where in factory class I do not want to declare two overload method. Using single method I can return ClassA or ClassB based on Data Model pass.
You could edit the Init method without having to edit anything else. This method will take a generic type parameter T, which can be of any type. Then you can use the is operator, which according to the docs used to type testing. You need to check however for any unsupported type of T, because you didn't add any constraint to the generic type passed. A raw implementation would be:
public class Factory
{
public ICommon Init<T>(T infoData)
{
if (infoData is DBInfoData dbInfoData) {
return new ClassA(dbInfoData);
}
if (infoData is WebInfoData webInfoData) {
return new ClassB(webInfoData);
}
throw new Exception($"Cannot create instance for info data of type {infoData.GetType().Name}");
}
}
And to test it:
var factory = new Factory();
var t1 = factory.Init(new DBInfoData()); // will be ClassA
var t2 = factory.Init(new WebInfoData()); // ClassB
To sophisticate it, you could introduce type constraint on your generic T class to make sure you can only pass appropriate types. For the current situation, you could create a marker interface for your classes DBInfoData and WebInfoData by introducing an empty interface say IInfoData. Then you have to inherit your classes like this:
public interface IInfoData {}
public class DBInfoData : IInfoData
{
public string Server { get; set; }
public string Database { get; set; }
}
public class WebInfoData : IInfoData
{
public string URL { get; set; }
public int Port { get; set; }
}
Now both inherits from (actually 'marked by') your base interface. Introduce a constraint to your factory to allow only descendants of IInfoData to be passed as an argument (so either DBInfoData or WebInfoData) by adding a constraint shown in the docs I linked above:
public class Factory
{
public ICommon Init<T>(T infoData) where T: IInfoData
{
if (infoData is DBInfoData dbInfoData) {
return new ClassA(dbInfoData);
}
if (infoData is WebInfoData webInfoData) {
return new ClassB(webInfoData);
}
throw new Exception($"Cannot create instance for info data of type {infoData.GetType().Name}");
}
}
Any type other than the descendants of IInfoData will cause a compilation error, and you're done. Use it like in my previous example:
var factory = new Factory();
var t1 = factory.Init(new DBInfoData()); // will be ClassA
var t2 = factory.Init(new WebInfoData()); // ClassB

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

Argument 'SpecificGroupType1' is not assignable to parameter type 'Group<IItem>'

I'm trying to put together an architecture like this:
Section
Group
Item
Attribute
Attribute
Group
Item
Attribute
Attribute
Section
[...]
I'm then trying instantiate this architecture like this:
var sections = new List<ISection>
{
new Section("Section Header", new List<Group<IItem>>
{
new SpecificGroupType1(token, "GroupName")
}
};
The SpecificGroupType1 then spins up a new list of the appropriate IItem type.
I'm getting the following error, though:
Argument SpecificGroupType1 is not assignable to parameter type Group<IItem>
I'm not quite sure why, though, because SpecificGroupType1 inherits from Group.
The full architecture looks like this (I omitted the IAttribute stuff, because the issue I'm running into happens before IAttribute stuff even gets involved):
Section.cs
public interface ISection { // Stuff }
public class Section : ISection
{
public Section(string sectionName, IList<Group<IItem>> groups)
{
Name = sectionName;
Groups = groups;
}
}
Group.cs
public interface IGroup { // Stuff }
public abstract class Group<T> : IGroup where T : IItem
{
protected Group(JToken token, string groupName)
{
Name = groupName;
Items = new List<IItem>();
foreach (var itemToken in Token.Children())
{
Items.Add((Item)Activator.CreateInstance(typeof(T), itemToken);
}
}
public string Name { get; internal set; }
public JToken Token { get; internal set; }
protected IList<IItem> Items { get; set; }
}
SpecificGroupType1.cs
public class SpecificGroupType1 : Group<SpecificItemType1>
{
public SpecificGroupType1(JToken token, string groupName) : base(token, groupName) {}
// Stuff
}
Item.cs
public interface IItem { // Stuff }
public abstract class Item : IItem
{
protected ConfigurationItem(JToken token)
{
Attributes = new List<IAttribute>();
Token = token;
}
public IList<IAttribute> Attributes { get; set; }
public JToken Token { get; set; }
}
SpecificItemType1.cs
public class SpecificItemType1 : Item
{
public SpecificItemType1(JToken token) : base(token) {}
// Stuff
}
Fundamentally, this is a problem with your generic parameters. Consider this simplified example.
// does not compile
Group<IItem> g = new SpecificGroupType1(token, "GroupName");
// does compile
Group<SpecificItemType1> g = new SpecificGroupType1(token, "GroupName");
The problem is that SpecificGroupType1 implements the class Group<SpecificItemType1>, which is not the same as Group<IItem>. If you want to be able to use more derived generic parameter types this way, you need to use a covariant generic parameter declaration. In C#, that's only possible on interfaces, not classes, so you may need to refactor a bit. It would be something like this.
interface IGroup<out T> : IGroup where T: IItem {
// declarations
}
Note the out keyword.

How to create a custom control factory method

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

Categories