Generic-inherited classes storing their parent ObservableCollection - c#

I have several collections, each of specific class. I need that every instance (model) store its parent collection (to be able to reach other instances because of data validation). Trying to find some smart solution - starting to doubt if there is such.
So far I came up with following.
Generic base-class:
public class ModelBase<T> : ObservableObject, IDataErrorInfo
{
public CustomObservableCollection<T> ParentCollection;
public int Id { get; set; } // some generic prop
}
Individual classes (models):
public class Model1: ModelBase<Model1>
{
public string Name { get; set; } // Some specific prop
// Data Error validation here, e.g. check of uniqueness
if (ParentCollection?.Where(c => c.Id != Id).Any(c => c.Name == Name) == true)
{
}
}
public class Model2: ModelBase<Model2>
{}
Custom ObservableCollection:
public class CustomObservableCollection<T> : ObservableCollection<T> where T : ModelBase<T>
{
public CustomObservableCollection() : base() { }
public CustomObservableCollection(List<T> list)
: base(list)
{
foreach (T item in list)
{
item.ParentCollection = this;
}
}
public void AddEdit(T item)
{
item.ParentCollection = this;
base.Add(item);
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
item.ParentCollection = this;
// Initialize values
}
// Generic method - I would like to call this directly from command handler regardless of type T
public void MoveItemUp(object parameter)
{
}
}
Usage:
CustomObservableCollection<Model1> Model1Collection = new (listOfModel1instances);
I've been also trying to use Interfaces, but without success.

Thank you for useful reaction, IVSoftware.
I've tried to implement it according your example, but I ran into other issue I forget to mention in the beginning.
In my CustomObservableCollection class I would like to have some generic methods processing collection belonging to Item given by argument, e.g.
public int GetMaxId(t item)
{
return item.Source.Max(i => i.Id);
}
What I get:
Error CS1061 'ICollection' does not contain a definition for 'Max' ...
(Given example does not give much sense. But let's assume, that all items have Order property. In command handler I want to reorder items in collection, e.g. move selected item up/down. In command handler all I have is just selected item given by command parameter.
This would allow me to have just one Command for all types of items.)

Your question (with the additional information you provided) indicates that the items in your collection require access to the parent collection and must provide an Id property, for example so that the parent collection can use the linq Max method.
One way to solve both objectives is to define an interface like ISourceAware and constrain CustomObservableCollection<T> to accept only items that implement it by adding where T: ISourceAware to its declaration.
public interface ISourceAware
{
ICollection Source { get; set; }
string Id { get; }
}
public class CustomObservableCollection<T> : ObservableCollection<T> where T: ISourceAware
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if(e.Action.Equals(NotifyCollectionChangedAction.Add))
{
foreach (ISourceAware iitem in e.NewItems)
{
iitem.Source = this;
#if DEBUG
var item = (Item)iitem;
// Try a loopback test
Console.WriteLine(
$"Index of {item.Description} is {item.ParentCollection.IndexOf(item)}");
#endif
}
}
}
}
The collection handles its own CollectionChanged notification to set itself as the Source of any item added to it. This addresses the primary issue of storing their parent ObservableCollection - you can use it for validation however you see fit.
A minimal example of a class that implements ISourceAware:
public class Item : ISourceAware
{
public string Description { get; set; } = string.Empty;
public ICollection Source { get; set; }
public CustomObservableCollection<Item> ParentCollection =>
(CustomObservableCollection<Item>)Source;
public string Id { get; set; }
= Guid.NewGuid().ToString(); // Automatic unique initialization if you want it.
}
Test
Here's the quick console code I wrote to test this answer.
internal class Program
{
static void Main(string[] args)
{
Console.Title = "Test ISourceAware";
var collection = new CustomObservableCollection<Item>();
collection.Add(new Item { Description = "Item 1" });
collection.Add(new Item { Description = "Item 2" });
Console.ReadKey();
}
}

Related

An issue with designing my interfaces in C# : Sample Project

I have a problem with designing my interfaces
I have these interfaces :
interface IField {
}
interface IScreenField : IField {
}
interface ITable {
CustomCollection<IField> CustomCollection { get; set; }
}
interface IScreen
{
AnotherCustomCollection<IScreenField> AnotherCustomCollection { get; set; }
}
IScreen interface should inherit from ITable but it shows an error that I have to implement a collection of IField but I already implement a collection of IScreenField
What is the soltuion for this ?
I uploaded a sample project to explain the issue more
You can check the error message in Screen class that says :
Screen does not implement interface member ITable.Fields. Screen.Fields cannot implement ITable.Fields becuase it does not have the matching return type of CusomCollection<IField>
Here is the sample :
Sample project
This description of this example helps you to solve the problem: If IExample2 inherits another Interface, when implementing IExample2 u need to implement
all the method(properties etc...) that has been declared in interface + the method of inhered interfaces from IExample2. Remember that when you implement an interface you have to implement all of members of that interface (you have to implement even the members of all interfaces that are in chain) and all the returns types has to be the same in interface and in class.
interface IExample
{
void Method1();
}
interface IExample2 : IExample
{
void Method2();
}
class Screen : IExample2
{
public void Method2()
{
}
public void Method1()
{
}
}
Chain Example
interface IExample
{
void Method1();
}
interface IExample2 : IExample
{
void Method2();
}
interface IExample3 : IExample2
{
void Method3();
}
This is the answer I posted on your previous post. Reposting it here as-is since your previous question was put on hold before I could
submit the answer.
It’s very difficult to identify the problem without looking at the full code but based on what you have said, I believe, you have implemented IScreenField explicitly and the compiler is not able to find any implementation of IField.
Checkout following for more info:
https://www.codeproject.com/Articles/1000374/Explicit-Interface-VS-Implicit-Interface-in-Csharp
Update: After looking at the code
First of all you need to understand difference between Implicit and Explicit implementations of an Interface:
Implicit: you access the interface properties and properties as if they were part of the class.
Explicit: you can only access properties and properties when treating the class as the implemented interface.
The problem with the Screen class is that it implements IScreen interface, which in-turn implements ITable interface. Both these interfaces have a property named Fields.
The problem surfaced due to this and you are required to
explicitly implement the Fields property in Screen class.
NOTE: It is irrelevant that you have different return types. Since you have implemented Fields property in both interfaces, it is assumed that you are expecting different values when the property is accessed from each of the interfaces.
public class Screen : IScreen
{
public string Name { get; set; }
AnotherCustomCollection<IBaseField> IScreen.Fields
{
get
{
return default(AnotherCustomCollection<IBaseField>);
}
}
CustomCollection<IField> ITable.Fields
{
get
{
return default(CustomCollection<IField>);
}
}
public string Title { get; set; }
public string Description { get; set; }
}
Now how to access them? To access Fields property of each of these Interfaces you need to access Screen object as those interfaces.
Ex:
var screen = new Screen();
var fields = screen.Fields; // Error
var fields = (screen as IScreen).Fields; // returns property value of IScreen Fields
var fields = (screen as ITable).Fields; // returns property value of ITable Fields
Here is the complete code: https://dotnetfiddle.net/5KS0Xd
Hope this was helpful. All the best and happy coding.
You could do something like this:
public class Screen : IScreen
{
public string Name { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public AnotherCustomCollection<IBaseField> Fields { get; set; }
CustomCollection<IField> ITable.Fields
{
get
{
throw new System.NotSupportedException();
}
}
}
And apparently the compiler likes that, and if you do something like this:
public static void Main(string[] args)
{
var collection = new List<IScreen>()
{
new Screen
{
Fields = new AnotherCustomCollection<ScreenInterface.IBaseField>
{
new TextField()
{
Name = "Hello"
}
}
}
};
var y = collection.First();
//Prints "Hello"
Console.WriteLine(string.Join(" ", y.Fields.Select(x => x.Name)));
Console.ReadLine();
}
But, if you are working with the upper interface (ITable)
public static void Main(string[] args)
{
var collection = new List<ITable>() //here
{
new Screen
{
Fields = new AnotherCustomCollection<ScreenInterface.IBaseField>
{
new TextField()
{
Name = "Hello"
}
}
}
};
var y = collection.First();
//Throws NotSupportedException
Console.WriteLine(string.Join(" ", y.Fields.Select(x => x.Name)));
Console.ReadLine();
}
My guess is that there isn't the concept of generic inheritance, and that may be proved if you switch the conditional generic parameter of AnotherCustomCollection from IBaseField to IField, and instead of throwing the exception, return the public Fields property on Screen.ITable.Fields. Compiler will automatically recognize the concrete property and everything will work.
So, for this to work, either define an implicit operator or a custom getter:
public class Screen : IScreen
{
public string Name { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public AnotherCustomCollection<IBaseField> Fields { get; set; }
CustomCollection<IField> ITable.Fields
{
get
{
var customCollection = new CustomCollection<IField>();
customCollection.AddRange(Fields);
return customCollection;
}
}
}

C# - Access property in inheriting class

I'm trying to access a generic typed property in a child class. In the below example I recreated my problem. Is there a workaround for this problem, or is it simply not possible? Thanks in advance!
EDIT: It's not possible to declare the collection as A<Model> or A<T>.
public abstract class Model {
public int Id { get; }
}
public interface I<T> where T: Model {
ICollection<T> Results { get; }
}
public abstract class A { }
public class A<T> : A, I<T> where T : Model {
public ICollection<T> Results { get; }
}
public class Example {
A[] col;
void AddSomeModels() {
col = new A[] {
new A<SomeModel>(),
new A<SomeOtherModel>()
}
}
void DoSomethingWithCollection() {
foreach (var a in col) {
// a.Results is not known at this point
// is it possible to achieve this functionality?
}
}
}
You can't do what you intend without some compromises.
First of all, you need to make your interface I<T> covariant in T:
public interface I<out T> where T : Model
{
IEnumerable<T> Results { get; }
}
The first compromise is therefore that T can only be an output. ICollection<T> isn't covariant in T so you'd need to change the type of Results to IEnumerable<T>.
Once you do this, the following is type safe and therefore allowed:
public void DoSomethingWithCollecion()
{
var genericCol = col.OfType<I<Model>>();
foreach (var a in genericCol )
{
//a.Results is now accessible.
}
}

Can't cast inherited class field which is a derived class

Before I explain my problem, keep in mind that I went for an architecture like this because this is going to be used in an inventory system under Unity, so I had to separate the Item which is a MonoBehavior which is just something in the world from its Data which are just values used for Inventory purposes... If that makes sense.
I have an architecture that goes like this:
public class ItemData
{
// some fields...
public string _name; //should be private with its properties but it doesn't matter in the current example
}
public class EquipmentData : ItemData
{
// some additional fields...
public float _weight;
}
public class Item
{
private ItemData _data;
//Properties
public virtual ItemData Data { get; set; }
}
public class Equipment : Item
{
//Properties
public override ItemData Data
{
get { return _data as EquipmentData; }
set { _data = value as EquipmentData; }
}
}
So, basically I have an item hierarchy that goes even deeper but 1 level is enough to explain myself. (It keeps going like Weapon : Equipment)...
The thing is, if I leave private ItemData _data; in Item class, and add a private EquipmentData _eData; in Equipment class, I will have the ItemData fields twice since EquipmentData inherits ItemData and so on for the other derived classes... getting it a third time if I have a class that derives from Equipment etc...
Something like:
public class Item
{
private ItemData _data;
}
public class Equipment : item
{
private EquipmentData _eData;
}
The fields of ItemData such as _name will appear twice in Equipment and I don't want that...
So I am guessing there is something wrong with my architecture, and there might be a way around this method that looks kind of "dirty", but I couldn't find anything specific for this problem online and I have reached my limits.
What I have tried:
I have tried using the keyword new in Equipment thinking I could hide the initial protected ItemData _data; that is in my Item class, and then have protected new EquipmentData _data; in Equipment but it obviously does not let me since to Shadow _data it needs to be the same type, doesn't seem to work with derived types.
Also, as shown in my code sample, I have tried overriding the property to return the proper type depending on the class it is called in, the casts always return null...
I still find it strange that I ended up trying to implement something like that, so I am open to new ideas to restructure things in a better way, or if someone has a solution I haven't thought of to keep things that way but make them work, it'd be very nice.
I hope I was detailed enough in my problem, if not I can clear things up if needed.
What You need is Generic Classes. By this, You can assign each Item its proper type of ItemData. So Equipment will have its EquipmentData assigned.
//TItemData is a name of this generic type.
//It could be T for example, just like variable name.
//These type names start from T by convention.
//This is not a new class or something like that.
//where TItemData : ItemData is a constraint,
//that assumes that this type should be a subtype of ItemData
public abstract class Item<TItemData> where TItemData : ItemData
{
protected TItemData Data;
}
//EquipmentData is a subtype of ItemData, so it fits here.
//No chances to write, for example, IntEquipment : Item<int> ,
//because int does not derive from ItemData.
public class Equipment : Item<EquipmentData>
{
//here Data will be of EquipmentData type, without any casting.
}
By implementing the above You will achieve Type Safety.
EDIT
To make a class that properly extends Equipment (let's call it a Weapon), and has its proper ItemData (let's call it WeaponData), You need to write something like this:
Edit Equipment, and make it abstract:
public abstract class Equipment<TEquipmentData>
: Item<TEquipmentData>
//this constraint is VERY important, as EquipmentData derives from ItemData, thus fulfill Item<TItemData> constraint.
where TEquipmentData : EquipmentData
{
//Data will have EquipmentData type here.
}
Create WeaponData
public WeaponData : EquipmentData
{
}
Create Weapon
public class Weapon : Equipment<WeaponData>
{
//Data will have WeaponData type here.
}
This works:
public class OverridePropertiesWithSameField
{
public void Test()
{
ChildItem ci = new ChildItem();
ChildItemData cid = new ChildItemData();
cid.ItemDataProp = "ItemDataProperty"; // Inherited
cid.ChildItemDataProp = "ChildItemDataProp"; // Specific
ci.ItemData = cid;
// You know you need ChildItemData type here.
var childItemData = ci.ItemData as ChildItemData;
string itemDataProp = childItemData.ItemDataProp;
string childItemDataProp = childItemData.ChildItemDataProp;
}
}
public class Item
{
protected ItemData data;
public virtual ItemData ItemData { get; set; }
}
public class ChildItem : Item
{
public override ItemData ItemData
{
get { return base.data; }
set { base.data = value; }
}
}
public class ItemData
{
public string ItemDataProp { get; set; }
}
public class ChildItemData : ItemData
{
public string ChildItemDataProp { get; set; }
}
You can use a generic type parameter, with a generic type constraint (where).
public class Item<DATA> where DATA : ItemData
{
public virtual DATA Data { get; set; }
}
Now your class can use a specific ItemData:
Item<ItemData> has property
public virtual ItemData Data { get; set; }
Item<EquipmentData> has property
public virtual EquipmentData Data { get; set; }
Item<ANOTHER> has property
public virtual ANOTHER Data { get; set; }

system.reflection unable to get property value

I have the following:
List<Agenda> Timetable;
public class Agenda
{
public Object item; //item can be of any object type but has common properties
}
class MasterItem
{
public long ID;
}
class item1:MasterItem { //properties and methods here }
class item2:MasterItem { //properties and methods here }
At the start of the code, I have a list of item which I added using
item1 sItem = new item1() { //Initialize properties with values }
Timetable.Add(new Agenda {item = sItem );
Here I want to get Agenda with Item that has ID=12. I tried using
object x = Timetable.Find(delegate (Agenda a)
{
System.Reflection.PropertyInfo pinfo = a.item.GetType().GetProperties().Single(pi => pi.Name == "ID"); //returned Sequence contains no matching element
return ....
}
Why does it return the error message "Sequence contains no matching element"?
I also tried
a.item.GetType().GetProperty("ID")
but it returns "Object reference not set to an instance of an object". It cannot find the ID.
It's funny that don't get much from googling ...
You are looking for a property but what you have is a field. A property has get/get accessors than can contain custom code (but usually don't) whereas a field does not. You can change your class to:
public class Agenda
{
public Object item {get; set;} //item can be of any object type but has common properties
}
class MasterItem
{
public long ID {get; set;}
}
However, you state
item can be of any object type but has common properties
If that's the case, then you should define an interface that they all implement. That way, you don't need reflection:
public class Agenda
{
public ItemWithID item {get; set;}
}
Interface ItemWithID
{
long ID {get; set;}
}
class MasterItem : ItemWithID
{
public long ID {get; set;}
}
class item1:MasterItem { //properties and methods here }
class item2:MasterItem { //properties and methods here }
Your code assumes public properties. Is this the case? You have omitted the most important part of the sample code. Without it, we cannot reproduce your issue.
Regardless, reflection is the wrong approach here. You should use the following syntax:
Timetable.Find(delegate(ICommonPropeeties a) { return a.ID == 12; });
Where ICommonPropeties is an interface implemented by all items.

c# How to implement a collection of generics

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.

Categories

Resources