I need some help with an example of Inheritance class (abstract). I can't use my function ToCSV() because i return a list of BananaPart and C# wants a list of FruitPart. How is it possible to solve this ?
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
FruitFactory fruitFactory = new FruitFactory();
Fruit myFruit = fruitFactory.Create("banana");
myFruit.FruitPart.ForEach(f => Console.WriteLine(f.ToString()));
}
}
public class FruitFactory {
public Fruit Create(string fruitType) {
switch(fruitType) {
case "banana":
return new Banana();
break;
default:
throw new Exception("undefined fruitType");
}
}
}
public abstract class Fruit {
protected string _name;
protected List<FruitPart> _fruitPart;
public Fruit() {
this._name = "A fruit";
}
public string Name { get { return this._name; } }
public List<FruitPart> FruitPart { get { return this._fruitPart; } }
}
public abstract class FruitPart { }
public class Banana : Fruit {
public Banana() : base() {
this._name = "banana";
this._fruitPart = ToCSV();
}
public List<BananaPart> ToCSV(){
return new List<BananaPart> { new BananaPart(5, "10"), new BananaPart(10, "20"), new BananaPart(20, "40") };
}
}
public class BananaPart : FruitPart {
private int _seed;
private string _dimensionPart;
public BananaPart (
int seed,
string dimensionPart
) {
this._seed = seed;
this._dimensionPart = dimensionPart;
}
}
It will be a pleasure for me to learn more about it ! Thank you in advance !
The reason that your code is not compiling is because List<T> is not covariant. In order to compile your code you need to change List<T> with IEnumerable<T> which has a covariant type parameter. So the changes are:
public abstract class Fruit {
protected string _name;
protected IEnumerable<FruitPart> _fruitPart;
public Fruit() {
this._name = "A fruit";
}
public string Name { get { return this._name; } }
public IEnumerable<FruitPart> FruitPart { get { return this._fruitPart; } }
}
You can learn more about covariance here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/
In short, covariance lets you deduce the assignment compatibility of two types based on the assignment compatibility of two other types. Since, we can assign a BananaPart to a FruitPart, we can deduce that a IEnumerable<BananaPart> can be assigned to IEnumerable<FruitPart>.
There is a problem in your model. Say I have a Banana, and it has a list of fruit-parts. Now I could call myBananana.FruitParts.Add(new ApplePart()), this cannot work since a banana is only composed of bananaparts.
To avoid this you need to be more restrictive in what you return. Instead of using a List<FruitPart> you could use IEnumerable<Fruitpart>. This avoids the problem since you cannot add anything to a IEnumerable.
If you are learning I would also recommend starting with interfaces instead of abstract classes. The later is sometimes called "Implementation inheritance", i.e. it is used to share code between two derived classes. This is sometimes useful, but in the majority of cases it is better to put this shared code in a third class, sometimes called "Composition over inheritance".
Your fruit could ook something like this using interfaces:
public interface IFruit
{
string Name { get; }
IEnumerable<IFruitPart> FruitPart { get; }
}
public class Banana : IFruit
{
public Banana() : base() => FruitPart = ToCSV();
public static List<BananaPart> ToCSV() => new List<BananaPart> { new BananaPart(5, "10"), new BananaPart(10, "20"), new BananaPart(20, "40") };
public string Name { get; } = "Banana";
public IEnumerable<IFruitPart> FruitPart { get; }
}
public interface IFruitPart { }
public class BananaPart : IFruitPart
{
public int Seed { get; }
public string DimensionPart { get; }
public BananaPart(int seed, string dimensionPart) => (Seed, DimensionPart) = (seed, dimensionPart);
}
Notice that you do not need much more code for the interface variant compared to the abstract class case.
You cannot safely cast List<Child> to List<Parent> because it would let you add Apple to List<Banana>.
You can create List<FuitPart> instead.
public List<FruitPart> ToCSV()
{
return new List<FruitPart> {
new BananaPart(5, "10"),
new BananaPart(10, "20"),
new BananaPart(20, "40") };
}
As List<BananaPart> doesn't inherite List<FruitPart> you can't do it like that.
I'll suggest you to make some wrapper classes, like FruitedDetails and BananaDetails that would inherite it. That way you could encapsulate the specific details of classes you are working with and wouldn't have to deal with various casting of objects when you pack your lists.
Related
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;
}
My brain is gonna to explode. :) So I would like to get help from you.
Please, think about my question like about just programmer puzzle. (Actually. perhaps it is very easy question for you, but not for me.)
It is needed to create array of objects. For example List where T is class. (I will describe Class T below). Also it is needed create “container” that will contain this array and some methods for work with this array. For example Add(), Remove(int IndexToRemove).
Class T must have field "Container", this way each elements of our array would be able to know where is it contained and has access its container's fields and methods. Notice, that in this case Class T should have type parameter. Indeed, it is not known beforehand which container's type is used.
Let us denote this class container as A and class element (class T) as AUnit.
Code:
class Program
{
static void Main(string[] args)
{
A a = new A();
a.Add();
a.Units[0].SomeField +=100;
Console.ReadKey();
}
}
class A
{
public List<AUnit> Units;
public A()//ctor
{
Units = new List<AUnit>();
}
public void Add()
{
this.Units.Add(new AUnit(this));
}
}
class AUnit
{
public int SomeField;
public A Container;
public string Name { get; private set; }
public AUnit(A container)
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
}
}
Public fields should be protected or private of course, but let think about this later.
You can ask “why we create public A Container field in AUnit”? We create field public string Name{get;private set;} (actually property but nevermind). And also we would like to be able to change value of this field for example method [Class AUnit] public bool Rename(string newName)();. The main idea of this method is changing Name field only that case if no one element in array (public List Units; ) has the same name like newName. But to achieve this, Rename method has to have access to all names that is currently used. And that is why we need Container field.
Code of extended version AUnit
class AUnit
{
public int SomeField;
public A Container;
public string Name { get; private set; }
public AUnit(A container)
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
}
public bool Rename(String newName)
{
Boolean res = true;
foreach (AUnit unt in this.Container.Units)
{
if (unt.Name == newName)
{
res = false;
break;
}
}
if (res) this.Name = String.Copy(newName);
return res;
}
}
Ok. If you still read it let's continue. Now we need to create Class B and class BUnit which will be very similar like Class A and Class Aunit. And finally the main question of this puzzle is HOW WE CAN DO IT? Of course, I can CopyPaste and bit modify A and AUnit and create this code.
class B
{
public List<BUnit> Units; //Only Type Changing
public B()//ctor Name changing...
{
Units = new List<BUnit>();//Only Type Changing
}
public void Add()
{
this.Units.Add(new BUnit(this));//Only Type Changing
}
}
class BUnit
{
public int SomeField;
public B Container;//Only Type Changing
public string Name { get; private set; }
public A a; //NEW FIELD IS ADDED (just one)
public BUnit(B container) //Ctor Name and arguments type changing
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
this.a=new A(); //New ROW (just one)
}
public bool Rename(String newName)
{
Boolean res = true;
foreach (BUnit unt in this.Container.Units) //Only Type Changing
{
if (unt.Name == newName)
{
res = false;
break;
}
}
if (res) this.Name = String.Copy(newName);
return res;
}
}
And I can to use this classes this way.
static void Main(string[] args)
{
B b = new B();
b.Add();
b.Units[0].a.Add();
b.Units[0].a.Units[0].SomeField += 100;
bool res= b.Units[0].a.Units[0].Rename("1");
res = b.Units[0].a.Units[0].Rename("1");
Console.ReadKey();
}
This construction is can be used to create “non-homogeneous trees”.
Help, I need somebody help, just no anybody…. [The Beatles]
I created B and BUnit using CopyPaste.
But how it can be done using “macro-definitions” or “Generic”, inherit or anything else in elegant style? (C# language)
I think that there is no reason to describe all my unsuccessful attempts and subquestions. Already topic is too long. : )
Thanks a lot if you still read it and understand what I would like to ask.
You need to implement a base type, lets call it UnitBase, with all common functionality. I'd structure your code the following way:
Create an interface for your container, this way you can change implementation to more performant solutions without modifying the elements you will be adding to the container.
public interface IContainer
{
Q Add<Q>() where Q : UnitBase, new();
IEnumerable<UnitBase> Units { get; }
}
Following the idea stated in 1, why not make the search logic belong to the container? It makes much more sense, as it will mostly depend on how the container is implemented:
public interface IContainer
{
Q Add<Q>() where Q : UnitBase, new();
IEnumerable<UnitBase> Units { get; }
bool Contains(string name);
}
A specific implementation of IContainer could be the following:
public class Container : IContainer
{
public Container()
{
list = new List<UnitBase>();
}
private List<UnitBase> list;
public Q Add<Q>() where Q: UnitBase, new()
{
var newItem = Activator.CreateInstance<Q>();
newItem.SetContainer(this);
list.Add(newItem);
return newItem;
}
public IEnumerable<UnitBase> Units => list.Select(i => i);
public bool Contains(string name) =>
Units.Any(unit => unit.Name == name);
}
Create a base class for your AUnit and BUnit types condensing all common functionality:
public abstract class UnitBase
{
protected UnitBase()
{
}
public IContainer Container { get; private set; }
public int SomeField;
public string Name { get; private set; }
public void SetContainer(IContainer container)
{
Container = container;
}
public bool Rename(String newName)
{
if (Container.Contains(newName))
return false;
this.Name = newName; //No need to use String.Copy
return true;
}
}
Implement your concrete types:
public class BUnit : UnitBase
{
public int SpecificBProperty { get; private set; }
public BUnit()
{
}
}
Shortcomings of this approach? Well, the container must be of type <UnitBase>, I've removed the generic type because it really wasn't doing much in this particular case as it would be invariant in the generic type.
Also, keep in mind that nothing in the type system avoids the following:
myContainer.Add<BUnit>();
myContainer.Add<AUnit>();
If having two different types in the same container is not an option then this whole set up kind of crumbles down. This issue was present in the previous solution too so its not something new, I simply forgot to point it out.
InBetween , I am very thankful to you for your advices. Actually I can't say that I understood your answer in full, but using your ideas I have done what I want.
Looks like my variant works well. However I would like to hear your (and everyone) opinions about code described below. The main goal of this structure is creating non-homogeneous trees. So could you estimate it from this side.
First of all. We need to create interfaces for both classes. We describe there all "cross-used" functions.
public interface IUnit<T>
{
string Name { get;}
void SetContainer(T t);
bool Rename(String newName);
}
public interface IContainer
{
bool IsNameBusy(String newName);
int Count { get; }
}
Next. Create Base for Unit Classes for future inheritance. We will use in this inheritors methods from Container Base so we need generic properties and IUnit interface.
class UnitBase<T> : IUnit<T> where T : IContainer
Unfortunately I don't know yet how to solve the problem with Constructor parameters. That is why I use method
SetContainer(T container).
Code:UnitBase
class UnitBase<T> : IUnit<T> where T : IContainer
{
protected T Container;
public string Name { get; private set; }
public UnitBase()
{
this.Name = "Default";
}
public void SetContainer(T container)
{
this.Container = container;
}
public bool Rename(String newName)
{
bool res = Container.IsNameBusy(newName);
if (!res) this.Name = String.Copy(newName);
return !res;
}
}
Next. Create ContainerBase
ContainerBase should:
1) has IContainer interface.
2)has information about what it will contain:
... where U : IUnit<C>, new()
3)and .... has information about what itself is. This information we need to pass as parameter to SetContainer() method.
Code ContainerBase:
class ContainerBase<U, C> : IContainer //U - Unit Class. C-Container Class
where U : IUnit<C>, new()
where C : ContainerBase<U, C>
{
protected List<U> Units;
public U this[int index] { get { return Units[index]; } }
public ContainerBase()//ctor
{
this.Units = new List<U>();
}
public void Add()
{
this.Units.Add(new U());
this.Units.Last().SetContainer(((C)this));//may be a bit strange but actualy this will have the same type as <C>
}
public bool IsNameBusy(String newName)
{
bool res = false;
foreach (var unt in this.Units)
{
if (unt.Name == newName)
{
res = true;
break;
}
}
return res;
}
public int Count { get { return this.Units.Count; } }
}
Cast ((TContainer)(this)) may be is a bit strange. But using ContainerBase we always should use NewInheritorContainer. So this cast is just do nothing…looks like...
Finally. This classes can be used like in this example.
class SheetContainer : ContainerBase<SheetUnit,SheetContainer> {public SheetContainer(){}}
class SheetUnit : UnitBase<SheetContainer>
{
public CellContainer Cells;
public PictureContainer Pictures;
public SheetUnit()
{
this.Cells = new CellContainer();
this.Pictures = new PictureContainer();
}
}
class CellContainer : ContainerBase<CellUnit, CellContainer> { public CellContainer() { } }
class CellUnit : UnitBase<CellContainer>
{
public string ValuePr;//Private Field
private const string ValuePrDefault = "Default";
public string Value//Property for Value
{
//All below are Just For Example.
get
{
return this.ValuePr;
}
set
{
if (String.IsNullOrEmpty(value))
{
this.ValuePr = ValuePrDefault;
}
else
{
this.ValuePr = String.Copy(value);
}
}
}
public CellUnit()
{
this.ValuePr = ValuePrDefault;
}
}
class PictureContainer : ContainerBase<PictureUnit, PictureContainer> { public PictureContainer() { } }
class PictureUnit : UnitBase<PictureContainer>
{
public int[,] Pixels{get;private set;}
public PictureUnit()
{
this.Pixels=new int[,]{{10,20,30},{11,12,13}};
}
public int GetSizeX()
{
return this.Pixels.GetLength(1);
}
public int GetSizeY()
{
return this.Pixels.GetLength(0);
}
public bool LoadFromFile(string path)
{
return false;
}
}
static void Main(string[] args)
{
SheetContainer Sheets = new SheetContainer();
Sheets.Add();
Sheets.Add();
Sheets.Add();
Sheets[0].Pictures.Add();
Sheets[1].Cells.Add();
Sheets[2].Pictures.Add();
Sheets[2].Cells.Add();
Sheets[2].Cells[0].Value = "FirstTest";
bool res= Sheets[0].Rename("First");//res=true
res=Sheets[2].Rename("First");//res =false
int res2 = Sheets.Count;
res2 = Sheets[2].Pictures[0].Pixels[1, 2];//13
res2 = Sheets[2].Pictures.Count;//1
res2 = Sheets[1].Pictures.Count;//0
res2 = Sheets[0].Pictures[0].GetSizeX();//3
Console.ReadKey();
}
Looks like it works like I want. But I didn’t test it full.
Let me say Thank you again, InBetween.
I have the following classes, and when I call CreateQuerySettings on the BaseScriptConfigurationList, it returns the new QuerySettings from ConfigurationList, rather than the HierarchicalQuerySettings value in BaseScriptConfigurationList:
public abstract class ConfigurationList<TConfigurationObject, TPropertyEnum>
{
public QuerySettings<TConfigurationObject, TPropertyEnum> CreateQuerySettings()
{
return new QuerySettings<TConfigurationObject, TPropertyEnum>();
}
}
public class BaseScriptConfigurationList : EditableConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
public BaseScriptConfigurationList(ConfigurationManager configurationManager)
: base(configurationManager, InternalAdminObjectType.BaseScript)
{
_BaseScriptPageListWatcher = new ConfigurationList<BaseScriptPageConfiguration, BaseScriptPageConfiguration.Property>.
ConfigurationWatcher(null);
_ConfigurationWatcher.ChildWatchers.Add(_BaseScriptPageListWatcher);
}
public new QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property, BaseScriptQueryChildrenSettings>();
}
}
Edit: I make the call from another class where TConfigurationObjectList is BaseScriptConfigurationList. I've added the constructor to the code above so you can see what it's doing. Please note that EditableConfigurationList inherits from ConfigurationList.
TConfigurationObjectList cl = (TConfigurationObjectList)typeof(TConfigurationObjectList).GetConstructor(new Type[] { typeof(ConfigurationManager) }).Invoke(new object[] { Manager.ConfigurationManager });
var querySettings = cl.CreateQuerySettings();
When I make this call, it goes into the ConfigurationList.CreateQuerySettings method.
How can I hide the CreateQuerySettings method, so that when I call it from the BaseScriptConfigurationList class, I get a HierarchicalQuerySettings object?
The new modifier can be beasty. Note that you are hiding and not overriding in your example. You are not showing that part of the code, but I assume you have this situation:
class Base
{
public static void BaseMethod() { Console.WriteLine("BASE!"); }
}
class Derived : Base
{
// Hides Base.BaseMethod()
new public static void BaseMethod() { Console.WriteLine("DERIVED!"); }
}
Base a = new Base();
a.BaseMethod(); // -> "BASE!"
Base b = new Derived();
b.BaseMethod(); // -> "BASE!"
Derived b = new Derived();
b.BaseMethod(); // -> "DERIVED!"
In BaseScriptConfigurationList.CreateQuerySettings()
you're return type is QuerySettings<T,T> so you will always get that type as a return value, but you are returning a HierarchicalQuerySettings. You can one, change the return type of CreateQuerySettings() to HierarchicalQuerySettings or two, cast the object to its child type "HierarchicalQuerySettings". If you really want to hide it, you can do this:
public class newclass : BaseScriptConfigurationList
{
public new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return (HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property>)base.CreateQuerySettings();
}
}
But that doesn't really seem efficient and i advise against it. Like i said, i maybe missing some other requirement, but based on the info that you gave..
Basically, what I'm seeing (and making assumptions) that TConfigurationObjectList Inhertis from ConfigurationList somewhere along the lines, so on and so forth, all the way up to EditableConfigurationList. since you are dynamically creating an instance of the class TConfigurationObjectList, and calling the method from that point, you will be calling the base ConfigurationList member CreateQuerySettings. You do not have access to the new CreateQuerySettings. If you are creating the class BaseScriptConfigurationList instance at this point, cast the object ((BaseScriptConfigurationList)cl).CreateQuerySettings(). That being said. if you do not know what you have at runtime:
var obj = typeof(TConfigurationObjectList).GetConstructor(new Type[] { typeof(ConfigurationManager) }).Invoke(new object[] { Manager.ConfigurationManager });
var cl = (obj as BaseScriptConfigurationList) ?? (TConfigurationObjectList)obj;
// or do something else
var querySettings = cl.CreateQuerySettings();
Note i am assuming your architecture is roughly set up like this:
public abstract class ConfigurationList<TConfigurationObject, TPropertyEnum>
{
public QuerySettings<TConfigurationObject, TPropertyEnum> CreateQuerySettings()
{
return new QuerySettings<TConfigurationObject, TPropertyEnum>();
}
}
public class TConfigurationObjectList : ConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
}
public class EditableConfigurationList<T, T1> : TConfigurationObjectList
{
protected EditableConfigurationList(ConfigurationManager configurationManager, object baseScript)
{
throw new NotImplementedException();
}
}
public class BaseScriptConfigurationList : EditableConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
public BaseScriptConfigurationList(ConfigurationManager configurationManager)
: base(configurationManager, InternalAdminObjectType.BaseScript)
{
}
public new QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property, BaseScriptQueryChildrenSettings>();
}
}
public class QuerySettings<T, T1>
{
}
public class HierarchicalQuerySettings<T, T1, T2> : QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
}
public class BaseScriptQueryChildrenSettings
{
}
public class BaseScriptPageConfiguration
{
public class Property
{
}
}
public class InternalAdminObjectType
{
public static object BaseScript { get; set; }
}
public class ConfigurationManager
{
}
public class BaseScriptConfiguration
{
public class Property
{
}
}
Create a base interface for the ConfigurationList class (say IConfigurationList) and use this interface as the data type for the variable cl instead of TConfigurationList.
I need a base class with a property where I can derive classes with the same property but different (compatible) types. The base Class can be abstract.
public class Base
{
public virtual object prop { get; set; }
}
public class StrBase : Base
{
public override string prop { get; set; } // compiler error
}
public class UseIt
{
public void use()
{
List<Base> l = new List<Base>();
//...
}
}
I tried it with Generics but that gives me a problem when using the class, because I want to store differently typed base classes in the List.
public class BaseG<T>
{
public T prop { get; set; }
}
public class UseIt
{
public void use()
{
List<BaseG> l = new List<BaseG>(); // requires type argument
//...
}
}
Here's an alternative approach to proposed solution:
public abstract class Base
{
public abstract void Use();
public abstract object GetProp();
}
public abstract class GenericBase<T> : Base
{
public T Prop { get; set; }
public override object GetProp()
{
return Prop;
}
}
public class StrBase : GenericBase<string>
{
public override void Use()
{
Console.WriteLine("Using string: {0}", Prop);
}
}
public class IntBase : GenericBase<int>
{
public override void Use()
{
Console.WriteLine("Using int: {0}", Prop);
}
}
Basically I've added a generic class in the middle that stores your properly-typed property. this will work assuming that you never need to access Prop from the code that iterates the members of the List<Base>. (You could always add an abstract method to Base called GetProp that casts the generic to an object if that's required.)
Sample usage:
class Program
{
static void Main(string[] args)
{
List<Base> l = new List<Base>();
l.Add(new StrBase {Prop = "foo"});
l.Add(new IntBase {Prop = 42});
Console.WriteLine("Using each item");
foreach (var o in l)
{
o.Use();
}
Console.WriteLine("Done");
Console.ReadKey();
}
}
Edit: Added the GetProp() method to illustrate how the property can be directly accessed from the base class.
You can't override the type of a property. Take a look at the following code:
StrBase s = new StrBase();
Base b = s;
This is completely valid code. But what happens when you try to do this?
b.prop = 5;
The integer can be converted to object, because everything is derived from object. But since b is actually a StrBase instance, it would have to convert the integer to a string somehow, which it can't. So that is why you aren't allowed to override the type.
The same principle applies to generics:
List<BaseG<object>> l = new List<BaseG<object>>();
BaseG<string> s = new BaseG<string>();
// The compiler will not allow this.
l.add(s);
// Here's the same problem, convert integer to string?
BaseG<object> o = l[0];
o.prop = 5;
This is because generic types in C# 2.0 are invariant. C# 4.0 does allow this type of conversions, called covariance and contravariance.
Solutions
An option is to cast the object back to string when you need it. You could add type validation in the subclass:
public class StrBase : Base
{
private string propValue;
public override object prop {
get
{
return this.propValue;
}
set
{
if (value is string)
{
this.propValue = (string)value;
}
}
}
}
You could also expose a type-safe property in the subclass:
public class StrBase : Base
{
public string strProp {
get
{
return (string)this.prop;
}
set
{
this.prop = value;
}
}
}
This is possible since C# 9.0
Beginning with C# 9.0, override methods support covariant return types.
(see Microsoft docs)
public class First
{
private int someV;
public virtual object SomeV { get => someV; set => someV = (int)value; }
public First() { }
}
public class Two : First
{
private string someV;
public override object SomeV { get => someV; set => someV = value.ToString(); }
public Two() { }
}
and use of those:
First firstClass = new First();
firstClass.SomeV = 1;
Two twoClass = new Two();
twoClass.SomeV = "abcd";
I have two data structure classes (this is a simplified version of my code)
Animal: Has one property “int Age”
Person: Has one property “DateTime Birthday”
What I am trying to accomplish is to compile “Uploading” (persisting to database), which is common across all different data structure classes.
So mainly my goal is to have a small Upload method that looks like
foreach (TypeName typeName in Enum.GetValues(typeof(TypeName)))
{
IDataPopulator populator =
new DataFactory().CreateDataPopulator(typeName);
populator.Populate(string.Empty);
}
But the problem is that, populator returns an object instances of different types, which I am having trying to encapsulate since they have no common properties.
(IDataPopulator.TResult Populate(string data) in the code below)
Is there a way to get around this? Or does Strategy pattern not fit for this kind of scenario?
Here is the code I’ve been working with
public class Program
{
public static void Main()
{
foreach (TypeName typeName in Enum.GetValues(typeof(TypeName)))
{
IDataPopulator populator = new DataFactory().CreateDataPopulator(typeName);
populator.Populate(string.Empty);
}
}
}
public enum TypeName { Person, Animal }
public class Person { public DateTime Birthday { get; set; } }
public class Animal { public int Age { get; set; } }
public interface IDataPopulator
{
TResult Populate(string data);
}
class AnimalDataPopulator : IDataPopulator
{
public Animal Populate(string data)
{
// create an instance of Animal using data
}
}
class PersonDataPopulator : IDataPopulator
{
public Person Populate(string data)
{
// create an instance of Person using data
}
}
public class DataFactory
{
public IDataPopulator CreateDataPopulator(TypeName typeName)
{
switch (typeName)
{
case TypeName.Person:
return new PersonDataPopulator();
case TypeName.Animal:
return new AnimalDataPopulator();
default:
throw new ArgumentOutOfRangeException("typeName");
}
}
}
public class UploadContext
{
private readonly IUploader _Uploader;
public UploadContext(IUploader uploader) { _Uploader = uploader; }
public void Upload() { _Uploader.Upload(); }
}
public interface IUploader
{
void Upload();
}
class PersonUploader : IUploader
{
private Person _Person;
public PersonUploader(Person person) { _Person = person; }
public void Upload()
{
Console.WriteLine("Uploading person...");
}
}
class AnimalUploader : IUploader
{
private Animal _Animal;
public AnimalUploader(Animal animal) { _Animal = animal; }
public void Upload()
{
Console.WriteLine("Uploading animal...");
}
}
I don't see how the Strategy pattern would fit here. The pattern is about a family of algorithms, e.g. line breaking strategies. You don't have strategies here, you have a single abstract method
TResult Populate(string data);
and several implementations of it.
I also don't understand the initial problem, maybe you want to queue several serialization operations? In that case the Command pattern is your friend.
I think what you are after is either serialization, if you are just storing blobs, or an ORM framework.