I have the following problem:
public interface IControlSingleContainer
{
ControlCollection Content { get; set; }
}
public interface IControlCollectionContainer
{
// I need to obtain a List of objects that implement IControlSingleContainer interface
List<IControlSingleContainer> Items { get; set; }
}
public class TabItem : IControlSingleContainer
{
public ControlCollection Content { get; set; }
}
public class TabbedContainer : IControlCollectionContainer
{
public List<TabItem> Items { get; set; } <- ERROR!
}
This code expect a List<IControlSingleContainer> in the property TabbedContainer.Items but I try to create classes with Items property that contains objects that implement IControlSingleContainer.
EDITED: Basically, the compilation error is the following:
'Cosmo.UI.Controls.TabbedContainer' does not implement interface member 'Cosmo.UI.Controls.IControlCollectionContainer.Items'. 'Cosmo.UI.Controls.TabbedContainer.Items' can not implement' Cosmo.UI.Controls.IControlCollectionContainer.Items' because it has the kind of matching return value of 'System.Collections.Generic.List <Cosmo.UI.Controls. IControlSingleContainer>'.
I explored a solution with generic interfaces but without any results...
Not entirely sure what you are trying to do here but you can't coerce a List<interface> into a List<concrete>. However, you can make your interface generic and add a constraint like this:
public interface IControlCollectionContainer<T> where T : IControlSingleContainer
{
List<T> Items { get; set; }
}
Now your class definition becomes this:
public class TabbedContainer : IControlCollectionContainer<TabItem>
{
public List<TabItem> Items { get; set; }
}
You're close,
public class TabbedContainer : IControlCollectionContainer
{
public TabbedContainer()
{
Items = new List<IControlSingleContainer>();
var t = new TabItem();
Items.Add(t);
}
public List<IControlSingleContainer> Items { get; set; }
}
This is one of the reasons explicit interface implementations exist depending on your use-case.
In your situation, you want your Items to be TabItem when working with that TabbedContainer directly. However, the interface requires Items to be a specific interface.
The trick is to declare TabbedContainer.Items and also IControlCollectionContainer at the same time, but reusing your TabItem class behind the scenes.
public class TabbedContainer : IControlCollectionContainer
{
public List<TabItem> Items { get; set; }
List<IControlSingleContainer> IControlCollectionContainer.Items
{
get
{
return // Your actual tab items
}
set
{
Items = //Whatever you need to do make sure you have actual
// TabItem objects
}
}
}
You'll need to update the sample above to actually handle setting/getting of interface's version of the items, but the main idea is to reuse your TabItem collection so they are always in sync.
What this actually does is when you are working with TabbedContainer and call Items, you will get a list of TabItem, but when working with your instance as an IControlCollectionContainer, your Items will return you the IControlCollectionContainer.Items instead.
Do note though that this can become quite a complex venture depending on how you are passing/modifying the instance of the container. It can be tricky to try and get these to sync if you are constantly modifying the items through TabbedContainer and IControlCollectionContainer declarations. Doing explicit implementations can sometimes help you take a step back and re-evaluate exactly what it is your end goal is and what types you declare on your properties.
For instance, if you aren't actually adding items to your interface's list, then why even use List? It could be better as an IEnumerable<T> or IReadOnlyCollection<T>.
Related
First of all apologize for long post nevertheless i wanted to highlight problem exactly and to be most readable and understandably. I am developing architecture of my program which will be responsible for files/databases data gather and face some architecture issues so far. All information step by step down below.
Let's consider following code below:
public interface IWatchService<TEntity> where TEntity : IEntity
{
IList<TEntity> MatchingEntries { get; set; }
}
public interface IWatchServiceDatabase<TEntity> : IWatchService<TEntity> where TEntity : IDatabaseEntity
{ }
public interface IWatchServiceFiles<TEntity> : IWatchService<TEntity> where TEntity : IFileEntity
{ }
class Database : IWatchServiceDatabase<DatabaseQuery>
{
public IList<DatabaseQuery> MatchingEntries { get; set; }
}
class Files : IWatchServiceFiles<CsvFile>
{
public IList<CsvFile> MatchingEntries { get; set; }
}
class Consumer
{
public IWatchService<IEntity> WatchService { get; set; }
public Consumer(IWatchService<IEntity> watchService)
{
WatchService = watchService;
var newList = WatchService.MatchingEntries;
}
public void AddNewEntries(IEntity entity) => WatchService.MatchingEntries.Add(entity);
}
class Program
{
static void Main(string[] args)
{
IWatchServiceDatabase<DatabaseQuery> db = new Database();
IWatchServiceFiles<CsvFile> filesCsv = new Files();
var dbConsumer = new Consumer(db); //cannot convert from 'IWatchServiceDatabase<DatabaseQuery>' to 'IWatchService<IEntity>'
var filesCsvConsumer = new Consumer(filesCsv); //cannot convert from 'IWatchServiceFiles<CsvFile>' to 'IWatchService<IEntity>'
dbConsumer.AddNewEntries(new DatabaseQuery());
dbConsumer.AddNewEntries(new CsvFile()); //illegal cause it's not FileConsumer !!!
filesCsvConsumer.AddNewEntries(new CsvFile());
filesCsvConsumer.AddNewEntries(new DatabaseQuery()); //illegal cause it's not DbConsumer !!!
}
}
public interface IEntity { }
public interface IFileEntity : IEntity
{
int Id { get; set; }
string Name { get; set; }
}
public interface IDatabaseEntity : IEntity { }
public class CsvFile : IFileEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class XmlFile : IFileEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DatabaseQuery : IDatabaseEntity { }
We have two errors there:
var dbConsumer = new Consumer(db);
var filesCsvConsumer = new Consumer(filesCsv);
Errors:
cannot convert from 'IWatchServiceDatabase' to 'IWatchService'
cannot convert from 'IWatchServiceFiles' to 'IWatchService'
This seems to be understandable because otherwise "we would be able" to add CsvFile or XmlFile to dbConsumer where generic IDatabaseEntity is expected and CsvFile and XmlFile are in fact IFileEntity and from the other hand DatabaseQuery to filesConsumer which expects IFileEntity and DatabaseQuery is IDatabaseEntity
//Database related
dbConsumer.AddNewEntries(new DatabaseQuery());
dbConsumer.AddNewEntries(new CsvFile()); //illegal cause it's not FileConsumer !!!
//Files related
filesCsvConsumer.AddNewEntries(new CsvFile());
filesCsvConsumer.AddNewEntries(new DatabaseQuery()); //illegal cause it's not DbConsumer !!!
From my understanding this is the clue why compiler raise those errors and which is fine. Therefore I've decided to overcome it in this way:
public interface IWatchService<out TEntity> where TEntity : IEntity
{
IEnumerable<TEntity> MatchingEntries { get; }
}
As can be seen i marked generic parameter out and changed IList to IEnumerable because IEnumerable can be only foreached. Without possibility to modify the list.
Now having this there is no possibility to modify MatchingEntries e.g Add() on therefore we are now not able to add e.g CsvFile (IFileEntity) where IDatabaseEntity is expected and vice versa DatabaseQuery (IDatabaseEntity) where IFileEntity is expected. Fine and understandably.
At the end i have two main questions:
What is the benefit to have this: IEnumerable MatchingEntries { get; } since it's {get;} it cannot be initialized or populated with values therefore i would always get empty list when calling that property. Or i am in wrong? Can somebody explain showing based on my code what can be done with it?
Let's imagine i want to have possibility to Add items to this MatchingEntries list and in Consumer class i want still to be able to pass in ctor either Database or Files related classes based on interfaces. How this can be accomplished? Please also show an example based on current code.
Many thanks for your support and hope someone benefit from it as i saw a lot of confusions related to that topic.
First question:
What is the benefit to have this: IEnumerable<T> MatchingEntries { get; } since it's {get;} it cannot be initialized or populated with values therefore I would always get empty list when calling that property. Or I am in wrong? Can somebody explain showing based on my code what can be done with it?
I am confused by the question. The interface says that a class that implements that interface must have a getter of this name and type. It says nothing at all about the contents of that sequence:
interface IFoo<out T>
{
IEnumerable<T> Bar { get; }
}
Now we can implement that interface however we want:
class TigerFoo : IFoo<Tiger>
{
public IEnumerable<Tiger> Bar
{
get
{
return new List<Tiger>() { new Tiger("Tony"), new Tiger("Terry") };
}
}
}
So why you think the returned sequence must be empty, I do not understand.
Similarly, nothing is stopping you from making a class that implements a setter:
class GiraffeFoo : IFoo<Giraffe>
{
public IEnumerable<Giraffe> Bar { get; set; }
}
…
GiraffeFoo gf = new GiraffeFoo();
List<Giraffe> giraffes = new List<Giraffe>() { new Giraffe("Gerry") };
gf.Bar = giraffes;
Nothing stops you from changing the contents of the list:
class TurtleFoo : IFoo<Turtle>
{
private List<Turtle> turtles = new List<Turtle>();
public IEnumerable<Turtle> Bar => turtles;
public void AddATurtle() => turtles.Add(new Turtle("Tommy"));
}
It is a mystery to me why you think you cannot do any of these things. You want to add a member to the collection? Write a method that adds a member to the collection. You just can't put it in the interface if you wan the interface to be covariant. But the interface tells you what services you must provide, not what services you must not provide! I do not understand why you think that an interface tells you what a class cannot do.
Since T is marked as out, you can now use any of these covariantly:
IFoo<Animal> ia1 = new TigerFoo();
IFoo<Animal> ia2 = new GiraffeFoo();
IFoo<Animal> ia3 = new TurtleFoo();
Of course you don't get to use the methods of the class once it is in an interface, but you never get to use the methods of a class once something is in an interface.
Second question:
Let's imagine I want to have possibility to Add items to this MatchingEntries list and in Consumer class i want still to be able to pass in ctor either Database or Files related classes based on interfaces. How this can be accomplished? Please also show an example based on current code.
Just write code that does that. I don't understand what the question is asking. Please clarify the question.
I'm trying to construct a class in c# (5.0) that I can use as a base class and it contains a List, but List could be 2 different types. I want to do the following:
public class BaseC
{
string header { get; set; }
List<object> recs { get; set; }
}
public class derive1: BaseC
{
List<myclassA> recs;
}
public class derive2: BaseC
{
List<myclassB> recs;
}
and importantly what I want to do is return the derived classes from a method in another class:
public BaseC PopulateMyDerivedClass()
{
BaseC b = new BaseC();
b.header = "stuff";
b.recs = FileHelperEngine<myclassB> fhe.ReadStringAsList(x);
}
the main point is that method PopulateMyDerivedClass really does the exact same thing for both derive1 and derive2, just that it returns a different type of list.
I think I need generics. But is that at the base class level and also is PopulateMyDerivedClass then supposed to return a generic? I think that perhaps I am not dealing with polymorhpism, but as you can guess I am new to generics, so struggling.
I think what you want is to make BaseC a generic class and specify the generic when defining the derived classes:
public class BaseC<T>
{
//...
virtual List<T> Recs { get; set; }
}
public class Derived1 : Base<MyClassA>
{
override List<MyClassA> Recs { get; set; }
}
Good point by Alexei Levenkov:
Usual note: DerivedX classes in this case will not have common parent unlike original sample. One may need to add more layer of classes (as non-generic parent of BaseC) or use an interface if DerivedX need to be treated as having common parent/interface.
I get the feeling that your code design could use some rethinking. For one, typically when we talk about "polymorphism", we are usually talking about polymorphic behaviors (methods), rather than members. I think you might want to consider two classes that implement an interface that does all the things you want each class to do (parses data into its own type of list and acts on it as you need it to).
Nevertheless, without getting way into the details of your code, I think something like this might be what you were trying to achieve:
public class BaseC<T>
{
string header { get; set; }
public List<T> recs {get;set;}
}
and
public BaseC<T> PopulateClass<T>()
{
var b = new BaseC<T>();
b.recs = new List<T>();
T first = (T)Convert.ChangeType("1", typeof(T));
b.recs.Add(first);
return b;
}
And to check our sanity:
BaseC<String> d1 = PopulateClass<String>();
System.Diagnostics.Debug.Print(d1.recs.First().ToString());
System.Diagnostics.Debug.Print(d1.recs.First().GetType().ToString());
BaseC<int> d2 = PopulateClass<int>();
System.Diagnostics.Debug.Print(d2.recs.First().ToString());
System.Diagnostics.Debug.Print(d2.recs.First().GetType().ToString());
prints
1
System.String
1
System.Int32
Take the following example code:
public abstract class ElementBase
{
}
public class ElementOne : ElementBase
{
}
public class ElementTwo : ElementBase
{
[XmlElement("element-one", typeof(ElementOne))]
[XmlElement("element-two", typeof(ElementTwo))]
public ElementBase[] SubElements { get; set; }
}
[XmlRoot("root-element")]
public class RootElement
{
[XmlElement("element-one", typeof(ElementOne))]
[XmlElement("element-two", typeof(ElementTwo))]
public ElementBase[] SubElements { get; set;}
}
The attributes on ElementOne.SubElements and ElementTwo.SubElements need to stay in sync (i.e., attributes added to one will need to be added to the other, and arguments need to stay the same), The reason for this is that in the xml, <element-one> and elements can both appear as subelements to <root-element> and <element-two>. The elements can be in any order, and the order is important. Also, there will probably be more subelements in the future. The way it is currently coded will make maintenance tedious and error-prone because of the need to maintain two separate places for attributes.
Is there a way to have these attributes "shared" between the two properties, such that a single edit will affect them both? I tried the following:
public class CommomAttribute : Attribute
{
public XmlElementAttribute f = new XmlElementAttribute("element-one", typeof(ElementOne));
public XmlElementAttribute l = new XmlElementAttribute("element-two", typeof(ElementTwo));
}
I then replaced the redundant attributes on the above classes' properties with a single [Command]. This didn't work.
An alternative question: is there a more elegant way to solve this problem?
You can try this if you don't mind having to go one level deeper to get to you sub-element items:
public abstract class ElementBase
{
}
public class ElementOne : ElementBase
{
}
public class ElementTwo : ElementBase
{
public SubElementList SubElements { get; set; }
}
public class SubElementList
{
[XmlElement("element-one", typeof(ElementOne))]
[XmlElement("element-two", typeof(ElementTwo))]
public ElementBase[] Items { get; set; }
}
[XmlRoot("root-element")]
public class RootElement
{
public SubElementList SubElements { get; set; }
}
Off the top of my head, I'd do the following:
On the ctor of each class (One and Two), require and instance of ElementBase and keep it as a private attribute (let's say, "SyncingElement")
Modify the setter of SubElements, to sync with the instance of "SyncingElement"
This way, SubElements on both objects would have the same memory address (same instance). So, if someone get the instance of SubElements from One modifies the object at index [2] (for example), it would affect SubElements at Two as well.
I have a worker class that does stuff with a collection of objects. I need each of those objects to have two properties, one has an unknown type and one has to be a number.
I wanted to use an interface so that I could have multiple item classes that allowed for other properties but were forced to have the PropA and PropB that the worker class requires.
This is the code I have so far, which seemed to be OK until I tried to use it. A list of MyItem is not allowed to be passed as a list of IItem even though MyItem implements IItem. This is where I got confused.
Also, if possible, it would be great if when instantiating the worker class I don't need to pass in the T, instead it would know what T is based on the type of PropA.
Can someone help get me sorted out?
Thanks!
public interface IItem<T>
{
T PropA { get; set; }
decimal PropB { get; set; }
}
public class MyItem : IItem<string>
{
public string PropA { get; set; }
public decimal PropB { get; set; }
}
public class WorkerClass<T>
{
private List<T> _list;
public WorkerClass(IEnumerable<IItem<T>> items)
{
doStuff(items);
}
public T ReturnAnItem()
{
return _list[0];
}
private void doStuff(IEnumerable<IItem<T>> items)
{
foreach (IItem<T> item in items)
{
_list.Add(item.PropA);
}
}
}
public void usage()
{
IEnumerable<MyItem> list= GetItems();
var worker = new WorkerClass<string>(list);//Not Allowed
}
You can make this work if you supply the interface directly instead of the concrete type. It just isn't able to do the implicit conversion for you:
IEnumerable<IItem<string>> items = GetItems().Cast<IItem<string>>();
var worker = new WorkerClass<string>(items);
On an aside: Your original code would actually work in C# 4, which supports covariance on IEnumerable<T>. But previous versions of C# don't, which is why you get the error.
This code compiles but looks very strange.
I have a typical and simple parent/child relationship here which is implemented using generics in a very strange way.
But I can't seem to find any other way of doing it.
class SampleObject<T> //I don't want to make this a generic but am forced to
{
//The SampleContainer this object is in
//This must be located in this base class
public SampleContainer<T> Parent { get; set; }
}
class SpecificObject : SampleObject<SpecificObject>
//SampleObject<SpecificObject> !!? This is the bizzare bit
//It seems really strange but necessary for compilation to work
{
}
//A class to contain a List of objects derived from SampleObjects
class SampleContainer<T>
{
public List<T> List;
}
class Start
{
public void Test()
{
SampleContainer<SpecificObject> container = new SampleContainer<SpecificObject>();
SpecificObject o = new SpecificObject(); //create an object
container.List.Add(o); //add it to the list
o.Parent = container; //set its parent
}
}
Can this code be simplified?
This seems to work without the type.
Is this what you were looking for?
class SampleObject //I don't want to make this a generic but am forced to
{
//The SampleContainer this object is in
//This must be located in this base class
public SampleContainer<SampleObject> Parent;//{ get; set; }
}
class SpecificObject : SampleObject
//SampleObject<SpecificObject> !!? This is the bizzare bit
//It seems really strange but necessary for compilation to work
{
}
//A class to contain a List of objects derived from SampleObjects
class SampleContainer<T>
{
public List<T> List;
}
class Start
{
public void Test()
{
SampleContainer<SampleObject> container = new SampleContainer<SampleObject>();
SpecificObject o = new SpecificObject(); //create an object
container.List.Add(o); //add it to the list
o.Parent = container; //set its parent
}
}
In the MSDN documentation, it states that:
When deriving from a generic base
class, you must provide a type
argument instead of the base-class's
generic type parameter:
public class BaseClass<T>
{...}
public class SubClass : BaseClass<int>
{...}
It's probably a constraint that the C# designers set up in the compiler. They require that a derived type must specify the type of the generic argument at compile time. I'm not quite sure why.
Generics can create some unwieldy class hierarchies. However, the syntax for SpecificObject : SampleObject does make sense, since you're stating that the object has a parent relationship. The only other way I could see you do this, would be to split out the hierarchy with an interface. It doesn't buy much, but it may help clarify the intent.
interface IHasParent<T>
{
T Parent { get; set; }
}
public class SpecificObject : IHasParent<SpecificObject>
{
public SpecificObject Parent { get; set; }
}
If you're concerned about how verbose your collection is, you can tame the angle brackets a bit by using:
public SpecificObjectContainer : Container<SpecificObject>
{
}