Getting an object's list from inside another object - c#

This is the layout
House -> HouseDef -> Room -> Door
L---> Windows
The problem is that any class may or may not have lists and nested classes like HouseDefinition does. The point is it should be flexible to handle any of these three cases for class variations:
1. hasList,
2. hasNestedObject with List inside that Nested Object
3. Has neither a List nor Nested class
Example of 1 being a Room class which contains a Window List
Example of 2 like House Class
Example of 3 like a Window Class
I have these two classes that I want to access generically from another class. I want to be able to get the Rooms List in House Definition by access of House class stored as an object in MyTreeNode. How can I do this not bound by types, or polymorphic to support a deeper hierarchy level in the future?
public class House
{
string name;
HouseDefinition definition;
public string Name() { return name; }
public HouseDefinition Definition {get {return definition;}}
public House(string name,HouseDefinition definition)
{
this.name = name;
this.definition = definition;
}
}
public class HouseDefinition
{
private List<Room> rooms = new List<Room>();
string type;
public List<Room> Rooms { get { return rooms; } }
public Room this[int i] { get { return rooms[i]; } }
public HouseDefinition(string type)
{
DefaultLayout();
this.type = type;
}
}
public class MyTreeNode : TreeNode
{
string label;
IEnumerable items;
bool hasList;
object item;
public string Label { get {return label; } }
public IEnumerable Items { get { return items;} }
public object Item { get { return item; } }
public bool HasList { get { return hasList; } }
public MyTreeNode(object item)
{
this.item = item;
label = item.ToString();
hasList = false;
}
public MyTreeNode(object item, IEnumerable Items)
{
this.item = item;
label = item.ToString();
hasList = true;
}
}

I think your classes look fine, but I would remove HouseDefinition, since those properties just seem like House to me. If you want to practice inheritance, you could create an IBuilding interface that forces House, Mansion, Shack to implement GetRooms() or something like that. A couple other things:
Your TreeNode class should be using generic types like public MyTreeNode(T item)
You should check out autoimplemented properties - the public fields automatically create private backing fields, so you don't need to create a private field and a getter like you did here: public object Item { get { return item; } }
it's considered bad practice to use the "object" type, so you should convert those to the generic types mentioned above
Good luck, your code is looking good so far!

Related

MVVM: ViewModels for bidirectional model elements

Assume I have following Model structure:
class Team {
public string Name {get;set; }
public List<Player> players {get;set;}
}
class Player {
public int Age {get;set;}
public string Name {get;set;}
public Team Team {get;set;}
}
I wish to create Viewmodels for this model. However, I also would like to avoid duplicating all properties from Player in the TeamVM and vice versa (for this simple example this would be feasable, but in reality rather cumbersome).
Looking at the literature and online articles, it seems that the "Pure" way would be to create a ViewModel for each Model and to have a ViewModel only return other ViewModels and never Models. This is all fine, but my problem is: how do you create these viewmodels without getting into a recursion trap. Assume I do it like this:
public class TeamVM: ViewModel<Team> {
private ObservableCollection<PlayerVM> _players;
public TeamVM(Team t): base(t) {
_players = new ObservableCollection();
foreach (Player p in t.players) {
_players.Add(new PlayerVM(t));
}
}
public string Name {
get { return _modelElement.Name; }
set { _modelElement.Name = value; NotifyPropertyChanged(); }
}
public ObservableCollection<PlayerVM> Players {
get { return _players; }
}
}
and
public class PlayerVM : ViewModel<Player> {
private TeamVM _teamVM;
public PlayerVM(Player p): base(p) {
_teamVm = new TeamVM(p.Team);
}
public int Age {
get { return _modelElement.Age; }
set { _modelElement.Age = value; NotifyPropertyChanged(); }
}
public string Name {
get { return _modelElement.Name; }
set { _modelElement.Name = value; NotifyPropertyChanged(); }
}
public TeamVM Team {
get { return _teamVM; }
set { _teamVm = value; NotifyPropertyChanged(); }
}
}
Obviously, the above can never work, since it creates recursion: creation of a TeamVM results in the creation of PlayerVMs which in turn spawn TeamVMs again etc.
Right now, I have solved this, by adding an intermediate class as follows:
public class TeamMinimalVM: ViewModel<Team> {
public TeamVM(Team t): base(t) {
}
public string Name {
get { return _modelElement.Name; }
set { _modelElement.Name = value; NotifyPropertyChanged(); }
}
}
public class TeamVM: TeamMinimalVM {
private ObservableCollection<PlayerVM> _players;
public TeamVM(Team t): base(t) {
_players = new ObservableCollection();
foreach (Player p in t.players) {
_players.Add(new PlayerVM(t));
}
}
}
And then having PlayerVM depend on TeamMinimalVM instead of TeamVM. This means that in the views, you would be able to do: {Binding Player.Team.Name} but not {Binding Player.Team.Players.Name}, which is kind of ok for me I guess since I don't think it's a great idea to do this anyway.
My question now is: is there a better/more "standard" way to do "Pure" VMs of bidirectional model elements? I do not want to clone properties of one type in the other (there are too many), nor do I want to expose Model elements directly.
Finally, the ViewModel class I use is this one (just for completeness, but it is not essential to the question I think.)
public class ModelElementViewModel<T> : ObservableObject where T : class
{
private bool _modelElementChanged;
private T _modelElement;
public ModelElementViewModel(T element)
{
_modelElement = element;
}
/// <summary>
/// The underlying model element for this viewmodel. Protected as one should not bind directly to model elements from the gui.
/// </summary>
internal T ModelElement {
get { return _modelElement; }
set {
if (_modelElement != value)
{
_modelElement = value;
ModelElementChanged = false;
NotifyAllPropertiesChanged();
}
; }
}
/// <summary>
/// Property that can be used to see if the underlying modelelement was changed through this viewmodel (note that an external
/// change to the model element is not tracked!)
/// </summary>
public bool ModelElementChanged {
private set
{
if (_modelElementChanged != value)
{
_modelElementChanged = value;
NotifyPropertyChanged();
}
}
get
{
return _modelElementChanged;
}
}
protected override void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
ModelElementChanged = true;
base.NotifyPropertyChanged(propertyName);
}
}
Edit:
What wasn't clear from my original question is that Players are not used exclusively by teams. I want following three scenarios to work:
I want to be able to create a view for a single player that displays all player information
I want to be able to create a view for a team, displaying the information of that team and a table of all players with their statistics
I also want to be able, for example, to have a Playersbook view, which consists of a table displaying all known players with their teamname for example.
Your classes have a clear hierarchy: teams aggregate players. Teams are owners, players are owned. Therefore, when creating a player VM, you can pass team VM as a constructor argument.
The obvious limitation of this is now you can't have players without teams. Possible solutions are: enforcing players to always be owned by some team; supporting null as a team VM and setting a proper value later; creating a "null team" object and using it for team-less players.
In cases like these, when there's a clear aggregation hierarchy, I use my OwnedObservableCollection<T, TOwner>. With it, I can create create a collection _players = new OwnedObservableCollection<PlayerVM, TeamVM>(this) in a team, then just add and remove players to and from the teams by using just Add and Remove.

Method with generic return type but not generic input. Is this possible?

Suppose we have a NodeData class:
public class NodeData<T>
{
public string Name;
public T Value;
public NodeData(string name, T value)
{
this.Name = name;
this.Value = value;
}
}
And a base Node class and child classes that have several properties with type NodaData:
public class Node
{
public List<NodeData<T>> listOutputs<T>()
{
var fieldInfos = GetType().GetFields();
var list = new List<NodeData<T>>();
foreach (var item in fieldInfos)
{
Type t = item.FieldType;
string name = item.Name;
if (t == typeof(NodeData<T>))
{
var output = new NodeData<T>(name, default(T));
list.Add(output);
}
}
return list;
}
}
public class TestNode : Node {
public NodeData<int> data;
public NodeData<double> data2;
public NodeData<double> data3;
public TestNode ()
{
data = new NodeData<int>("test", 111);
data2 = new NodeData<double>("test", 113);
}
}
As you can see there is a method which lists all outputs with type T in the Node class So I can find what are the output fields of the child class in runtime:
TestNode node = new TestNode ();
var list = node.listOutputs<int>(); // this returns data
But I need to know how to use this method to list all NodeOutputs of any type T. In this example int and double. Do I need to add a method with this signature public List<NodeData<T>> listOutputs() // should return all properties data, data2, data3. Is it possible to have method like this? return type is generic but there is no type argument for method.
Even after your edit(s) it is not entirely clear what you are trying to achieve but here are my assumptions:
-You want to have some kind of Node object that acts as a container for different types of NodeData elements.
-You want to be able to return one list from this Node object that contains all NodeData elements stored in the Node container, regardless of the NodeData objects' type.
Instead of returning a List> object from the listOutputs methods, just return the non-generic version of the List object. Then you don't have to deal with T in the method call.
The logic that loops through the objects in the non-generic list can then examine the type to process the contained NodeData objects correctly.
Important note: My proposed solution is by no means pretty but I think it answers the question. In my opinion something is already seriously flawed from an OO point of view in the presented code (e.g. use of reflection) and a better solution would have to start by changing the underlying data structures. But that can only be done if we have more information how this is to be used, e.g. what kind of logic consumes the returned list.
You can create a base interface that will be used to return the generic data.
public interface INodeData
{
string Name { get; }
}
public class NodeData<T> : INodeData
{
public string Name { get; private set; }
public T Value { get; private set; }
public NodeData(string name, T value)
{
this.Name = name;
this.Value = value;
}
}
I modified the function to return a list of the interface. Doing this you won't depend on T.
public class Node
{
public List<INodeData> listOutputs()
{
var fieldInfos = GetType().GetFields();
var list = new List<INodeData>();
foreach (var item in fieldInfos)
{
INodeData data = GetType().GetField(item.Name).GetValue(this) as INodeData;
list.Add(data);
}
return list;
}
}
If you test the method, it should return the fields in a list. To work with a specific type, you can make use of is before using the type you search for.
public class TestNode : Node
{
public NodeData<int> data;
public NodeData<double> data2;
public NodeData<double> data3;
public TestNode()
{
data = new NodeData<int>("test", 111);
data2 = new NodeData<double>("test", 113);
}
}
private static void Main(string[] args)
{
TestNode node = new TestNode();
var list = node.listOutputs(); // this returns data
}
This may well be an XY problem, in that you probably want to rethink how you are designing your classes because using reflection in this way doesn't seem right. But give the problem you've presented, I'd tackle it like this:
public abstract class NodeDataBase
{
public string Name { get; set; }
public NodeData(string name)
{
this.Name = name;
}
// this isn't actually needed, but might be helpful
public abstract object GetValue();
}
public class NodeData<T> : NodeDataBase
{
public T Value { get; set; }
public NodeData(string name, T value) : base(name)
{
this.Value = value;
}
public override object GetValue()
{
return Value;
}
}
And now your method signature would be:
public List<NodeDataBase> listOutputs()
And with the list returned, you can use the GetValue method to get the actual values without needing to cast to the right generic type to be able to get at the Value property.
You could also just have a return type of List<object>, but then you'll have to cast each member of that list to the right generic type before you can access it's properties.
You can also avoid that nasty reflection code, instead of having data, data1, and data2, you could simply do this in your Node class:
public class Node
{
public List<NodeDataBase> Data { get; protected set; }
public Node()
{
Data = new List<NodeDataBase>();
}
}
And now you don't even need your listOutputs method because you can just get the list from the node (unless you actually wanted a copy, but that's fairly trivial to implement).
And you TestNode would be just:
public class TestNode : Node {
public TestNode ()
{
Data.Add(new NodeData<int>("test", 111));
Data.Add(new NodeData<double>("test", 113));
}
}

Using Interface Type as generic

I have a class which must implement the following property
public ICollection<IType> Items
{
get { return this.items;}
}
My question is how to implement this when the type of this.items is a List<MyType> where MyType implements IType. I need to ensure the following:
Unecessary enumeration of the list is avoided if possible
That the class can internally treat the elements of this.items as their concrete type
That external callers may add and remove elements to this collection
Thanks in advance.
How about Items being IEnumerable<IType>? IEnumerable is covariant so the code would just work with no changes. On the other hand, you could have another, dedicated method to add elements to the internal list.
class MainClass
{
public static void Main()
{
ShowMeHowToDoIt show = new ShowMeHowToDoIt();
show.Add( new TheType() );
foreach ( var item in show.Items )
{
Console.WriteLine( item );
}
}
}
public class ShowMeHowToDoIt
{
private List<TheType> items = new List<TheType>();
public void Add( TheType item ) { items.Add( item ); }
public IEnumerable<IType> Items
{
get { return items; }
}
}
public interface IType { }
public class TheType : IType { }
Like Paul mentioned, you can't have both #2 and #3. You'll have to pick one or the other, or expose the concrete type to external callers. But, for your actual requirement, your best bet is to store your collection as a List internally, and just use a method when you need to get a member by the concrete type. Something like this:
private List<IType> items = new List<IType>();
private TType GetItem<TType>(int index)
where TType : IType
{
return (TType)items[index];
}
public ICollection<IType> Items
{
get
{
return this.items;
}
}
As pointed out by #PaulPhillips in the comments to this question:
Requirements (2) and (3) are contradictory.
One approach is to change the type of Items to IEnumerable<IType> and have another property of ICollection<MyType>. This will mean some redesign but clearly I was going about this wrong anyway.
Thanks!
Either declare this.items as a List<IType> if you want to expose it as ICollection<IType> and thus allowing external callers to add ITypes that are not MyTypes.
Internally work like this on the items of the list
var myObj = this.items[i] as MyType;
if (myObj == null) {
work with this.items[i] and treat it as a IType
} else {
work with myObj which is a MyType
}
OR
declare the public property as
public ICollection<MyType> Items { get return this.items; } }
and thus allow external callers to add only items of type MyType.
I am sorry, but you cannot fulfill conditions (2) and (3) at the same time
UPDATE
Another option is to only allow external callers to get items of the list but not to add items, by using an indexer having only a getter.
public IType this[int i]
{
get { return this.items[i]; }
}
an external caller can then access items like this
var obj = new ClassImplementingThisStuff();
int i = 5;
IType x = obj[i];
Also add a count property
public int Count {
get { return this items.Count; }
}
This solution avoids unnecessary enumeration.
I think the points in the comments about this being possibly a bad design are valid, however you can still do something like this and get away with it:
interface IFruit
{
string Name { get; }
string SerialNumber { get; }
}
class Apple : IFruit
{
private string _serial = Guid.NewGuid().ToString();
public string Name {
get {
return "Apple";
}
}
public string SerialNumber {
get { return _serial; }
}
}
class AppleBasket : IEnumerable<IFruit>
{
private List<Apple> _items = new List<Apple>();
public void Add(Apple apple) {
_items.Add(apple);
}
public IEnumerator<IFruit> GetEnumerator() {
return _items.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return _items.GetEnumerator();
}
}
/******************/
AppleBasket basket = new AppleBasket();
Apple apple1 = new Apple();
basket.Add(apple1);
Apple apple2 = new Apple();
basket.Add(apple2);
foreach (IFruit fruit in basket) {
Console.WriteLine(fruit.SerialNumber);
}
I would recommend you rethink your approach though.

How to create a property for a List<T>

private List<T> newList;
public List<T> NewList
{
get{return newList;}
set{newList = value;}
}
I want to create something like this, but this is won't work. it's just an example to demonstrate my goal as it's pretty common creating proprties for string and int and even T but I've never seen a List property
Is it even possible do such a thing, creating a property for type List ?
EDIT
I have a normal class that has normal properties (string properties, int properties, etc) but I have this property that stores user options, So on the presentation layer I had to convert them into a string so I could be able to store them in the Object. Now is it possible to have a property of type List to store the multivalues in a better and clean way, instead of converting information into one string and then split it and again join it! Thanks Again =D
EDIT2
private List<KeyValuePair<string, string>> _settings;
public List<KeyValuePair<string, string>> MySettings
{
get { return _settings; }
set { _settings = value; }
}
I used the exact code you posted but the property still won't appear in the object's instance, so I tried adding code in the get and set (I wonder why you left them empty or does it means something?) and also added a private variable in the class but still it doesn't appear in the properties of the object's instance!
I hope you could provide the exact code to implement this property and a simple code that assigns or retrieves from/to an instance of this class object
It's the first time to even hear about this KeyValuePair and all the tutorials are pretty simple and not for my case, sorry!
The Last Edit: After a lot of researching and the help of Mark Avenius I found the perfect answer. hope everyone can benefit from this.
NOW! HOW TO CREATE A PROPERTY FOR A LIST :
The Options Class
Public Class Options
{
private string id;
private int option;
public int ID
{
get { return id; }
set { id= value; }
}
public string Option
{
get { return option; }
set { option = value; }
}
}
The Users Class
public class Users
{
private int userId;
private string pass;
private List<Options> userOptions = new List<Options>();
public int ID
{
get { return userId; }
set { user = userId; }
}
public string Pass
{
get { return pass; }
set { pass = value; }
}
public List<Options> OptionsList
{
get { return userOptions; }
set { userOptions = value; }
}
}
The Presentation Layer
Users newUser = new Users ();
Options userOption = new Options ();
userOption.ID = int.Parse(txtBxID.Text);
userOption.Option = txtBxOption.Text;
Item.Options.Add(userOption);
T must be defined within the scope in which you are working. Therefore, what you have posted will work if your class is generic on T:
public class MyClass<T>
{
private List<T> newList;
public List<T> NewList
{
get{return newList;}
set{newList = value;}
}
}
Otherwise, you have to use a defined type.
EDIT: Per #lKashef's request, following is how to have a List property:
private List<int> newList;
public List<int> NewList
{
get{return newList;}
set{newList = value;}
}
This can go within a non-generic class.
Edit 2:
In response to your second question (in your edit), I would not recommend using a list for this type of data handling (if I am understanding you correctly). I would put the user settings in their own class (or struct, if you wish) and have a property of this type on your original class:
public class UserSettings
{
string FirstName { get; set; }
string LastName { get; set; }
// etc.
}
public class MyClass
{
string MyClassProperty1 { get; set; }
// etc.
UserSettings MySettings { get; set; }
}
This way, you have named properties that you can reference instead of an arbitrary index in a list. For example, you can reference MySettings.FirstName as opposed to MySettingsList[0].
Let me know if you have any further questions.
EDIT 3:
For the question in the comments, your property would be like this:
public class MyClass
{
public List<KeyValuePair<string, string>> MySettings { get; set; }
}
EDIT 4: Based on the question's edit 2, following is how I would use this:
public class MyClass
{
// note that this type of property declaration is called an "Automatic Property" and
// it means the same thing as you had written (the private backing variable is used behind the scenes, but you don't see it)
public List<KeyValuePair<string, string> MySettings { get; set; }
}
public class MyConsumingClass
{
public void MyMethod
{
MyClass myClass = new MyClass();
myClass.MySettings = new List<KeyValuePair<string, string>>();
myClass.MySettings.Add(new KeyValuePair<string, string>("SomeKeyValue", "SomeValue"));
// etc.
}
}
You mentioned that "the property still won't appear in the object's instance," and I am not sure what you mean. Does this property not appear in IntelliSense? Are you sure that you have created an instance of MyClass (like myClass.MySettings above), or are you trying to access it like a static property (like MyClass.MySettings)?
Simple and effective alternative:
public class ClassName
{
public List<dynamic> MyProperty { get; set; }
}
or
public class ClassName
{
public List<object> MyProperty { get; set; }
}
For differences see this post: List<Object> vs List<dynamic>
public class MyClass<T>
{
private List<T> list;
public List<T> MyList { get { return list; } set { list = value; } }
}
Then you can do something like
MyClass<int> instance1 = new MyClass<int>();
List<int> integers = instance1.MyList;
MyClass<Person> instance2 = new MyClass<Person>();
IEnumerable<Person> persons = instance2.MyList;
You could do this but the T generic parameter needs to be declared at the containing class:
public class Foo<T>
{
public List<T> NewList { get; set; }
}
It's possible to have a property of type List<T> but your class needs to be passed the T too.
public class ClassName<T>
{
public List<T> MyProperty { get; set; }
}
Either specify the type of T, or if you want to make it generic, you'll need to make the parent class generic.
public class MyClass<T>
{
etc

What is the correct way to write a constructor for a wrapper class?

I'm writing a plugin for an application with a .NET API. The objects of the program can have custom attributes applied through two methods of the root object type which assign key/value pairs to the objects.
BaseAppObject.GetUserString(string key, string value);
BaseAppObject.SetUserString(string key, ref string value);
I'm creating a set of my own custom classes that act as wrapper classes around instances of BaseAppObject. All my classes are derived from a class Node which has a field to store a reference to a BaseAppObject. Other properties of Node and types that derive from Node use the GetUserString and SetUserString methods of the associated BaseAppObject instance to read or write property values directly to or from the associated BaseAppObjects.
This way when the application is closed all the information needed to regenerate these wrapper classes later is stored in the actual document.
Here's a simplified version of what I have for my base class constructor:
public abstract class Node
{
BaseAppObject _baseObject;
public Node(BaseAppObject baseObject, string name)
{
this._baseObject = baseObject;
this.Name = name;
}
public string Name
{
get {
string name = "";
_baseObject.GetUserString("CPName", ref name);
return name;
}
set {
_baseObject.SetUserString("CPName", value);
}
}
}
Other classes derived from Node may add additional properties like this.
public CustomClass:Node
{
public CustomClass(BaseAppObject baseObj,string name, string color):base(baseObj,name)
public string Color
{
get {
string name = "";
this.BaseObject.GetUserString("Color", ref name);
return name;
}
set {
this.BaseObject.SetUserString("Color", value);
}
}
}
I'm trying to figure out the best way to setup the constructors and other methods of my classes to initiate and regenerate instances of my classes. I need to be able to create new instances of my classes based of clean instances of BaseAppObject that have no user strings defined, and also regenerate previously existing instance of my class based on the user strings stored in a existing BaseAppObject.
It looks like you have already figured out how to regenerate previously existing classes. To generate a clean object with no values, all you have to do is provide an additional overload that takes no parameters and creates _baseObject. Once the object is created in this manner, you can use it's properties to set the member values.
public abstract class Node
{
BaseAppObject _baseObject;
//Empty constructor
public Node()
{
BaseAppObject = new BaseAppObject();
}
public Node(BaseAppObject baseObject, string name)
{
this.BaseObject = baseObject;
this.Name = name;
}
public string Name
{
get {
string name = "";
_baseObject.GetUserString("CPName", ref name);
return name;
}
set {
_baseObject.SetUserString("CPName", value);
}
}
}
public CustomClass:Node
{
// Empty constructor
public CustomClass() : Base() {}
public CustomClass(BaseAppObject baseObj,string name, string color):base(baseObj,name)
public string Color
{
get {
string name = "";
this.BaseObject.GetUserString("Color", ref name);
return name;
}
set {
this.BaseObject.SetUserString("Color", value);
}
}
}
Sample code for creating an empty object and populating it:
CustomClass node = new CustomClass();
node.Name = "foo";
node.Color = "bar";
I have written a base Wrapper Object that I have all my wrappers inherit from.
public abstract class BaseWrapper<TBaseType>
where TBaseType : class
{
protected readonly TBaseType BaseItem;
protected BaseWrapper(TBaseType value)
{
BaseItem = value;
}
public bool IsNotNull()
{
return BaseItem != null;
}
}
I don't see why you don't want to make Node a child class of BaseClassObj. If you're writing a wrapper class, you can use the type-casting features to make your "Node" class and all it's children directly compatible with the BaseClassObj.
If you're doing that, you can just say
public abstract class Node : BaseAppObject
{
public Node(string name) : base()
{
this.Name = name;
}
public string Name
{
get {
string name = "";
this.GetUserString("CPName", ref name);
return name;
}
set {
this.SetUserString("CPName", value);
}
}
}
Now you can use a Node object just like a BaseClassObj, and still use the additional functionality. I think this is a better way to implement a wrapper class. The typecasting in C# is fantastic and you should take advantage of it.

Categories