c# parent child design - c#

I am designing a c# class and would like to know if my design is right.
abstract class PersonBase
{
public string Name { get; set; }
public PersonBase Parent { get; set; }
public List<PersonBase> Children { get; set; }
}
class Person : PersonBase
{
//public override List<Person> Children { get; set; }
public Person()
{
Children = new List<PersonBase>();
}
public void Add(Person child)
{
child.Parent = this;
Children.Add(child);
}
}
test code:
private void button1_Click(object sender, EventArgs e)
{
Person parent = new Person();
parent.Name = "parent";
Person child = new Person();
child.Name = "child1";
child.Add(new Person() { Name = "grandchild1" });
parent.Add(child);
}
It works as expected. I am able to access the parent children objects from anywhere in the hierarchy. My concern is it looks recursive or circular reference (can't find the right word here).
Here is what I did finally:
public class Person
{
public string Name { get; set; }
public Person Parent { get; set; }
public List<Person> Children { get; private set; }
public Person()
{
Children = new List<Person>();
}
public void AddChild(Person child)
{
if (child == this) { return; }
if (Children.Contains(child)) { return; }
child.Parent = this;
Children.Add(child);
}
}

Like #LukeH asked your break out of BasePerson and Person do not make sense. This will work just fine.
public class Person
{
public string Name { get; set; }
public Person Parent { get; set; }
public IList<Person> Children { get; private set; }
public Person()
{
Children = new List<Person>();
}
public void Add(Person child)
{
child.Parent = this;
Children.Add(child);
}
}
If you are going to have different kinds of Person then you might want to break you stuff out into an Interface if there is not inherited logic and an abstract class if you want to provide some default logic.
EDIT: Adding expressed issues from Danny

It could become a problem if you used child.Add(Me or MyParent or ancestor). Then it would be an endless loop of references. You might want to add code in the Add method to prevent improper usage so a 'Person' can not add itself or it's parents as a child.

The base class seams redundant, also, you are assuming children is not null and assigning in constructor, yet it has a public setter.
Change children's setter to private or protected and don't worry about circular reference - just mark parent property with an attribute to prevent it from being serialized.

//Declare and initialize Person a, b, c;
a.Add(c);
b.Add(c); // Now a thinks c is a child, but c does not think a is a parent!
You need some sort of validation, perhaps that c doesn't already have a parent, or set the parent/child only in a Parent.CreateChild method. Or allow it to have multiple parents.
(Also, I would declare the method AddChild, since that's what it's doing. And also pay attention to the design considerations from other commenters.)
Perhaps this:
class Person
{
public string Name { get; private set; }
public Person Parent { get; private set; }
public IList<Person> Children { get; private set; }
private Person() {} // Private constructor
public static Person CreatePersonNoParent(string name){*implementation elided*};
public Person CreateChild(string name)
{
Person child = new Person { Name=name, Parent=this };
this.Children.Add(child);
return child;
}
}

Related

Less visibility in child property c#

I have a base class with a public property.
public class Parent
{
public string Name {get; set;}
}
I want that field will be calculated in the child constructor
public class Child1 : Parent
{
public Child1(string a, string b)
{
this.Name = a + " " + b;
}
}
And I want to mask or decrease visibility of Name in the child.
That we will not be able to do
var l = new Child1("aa", "bb");
l.Name = "something else";
I am pretty sure that what I am trying to achieve is impossible and against Liskov substitution principle.
Any idea about this?
This sounds less like a parent/child relationship or another case of Polymorphy.
I do not know of a accesor that would say "public in this class, but not inheritors". And not just for C#, but any OOP langauge I learned.
It sounds more like a a case for encapsulation:
public class Child1 {
Parent _backingField; //Set in constructor
public string ParentName {
get { return _backingField.Name; }
//No setter
}
}
One of the first things you learn in MVVM: If polymorphy can not do that or you do not have enough control over the class, just encapsulate it into something you do have full control over.
Edit:
In a more advanced view, with a abstract base class:
public abstract class Person {
public string Name {get; set;}
}
public class Parent : Person {
//I know of no property parent could have, that person does not
//Even the concept of a parent name, sounds more like a "Person" thing
}
public class Child1 : Person {
Parent _backingField; //Set in constructor
public string ParentName {
get { return _backingField.Name; }
//No setter
}
}
Alternatively, just make one class:
public class Person {
//Every person has a parent
Person _backingField; //Set in constructor
public string ParentName {
get { return _backingField.Name; }
//No setter
}
//And of course it's own name
public string Name {get; set;}
}
Why not passing name to Parent constructor like that?
public class Parent
{
public string Name { get; private set; } // private set; can be skipped
public class Parent(string name)
{
Name = name;
}
}
Then Child class would look like this
public class Child : Parent
{
public Child(string a, string b)
: base(a + b)
{ }
}
If you are not able to make change in base class there is alternative solution (partially only)
public class Parent
{
public string Name { get; set; }
}
public class Child : Parent
{
public new string Name
{
get => base.Name;
private set => base.Name = value;
}
public Child(string a, string b)
{
Name = $"{a} {b}";
}
}
But there is quick workaround for encapsulation...
Child c = new Child("abc", "def");
c.Name = "New name"; // this will cause compilation error
Parent p = c; // this is perfectly legal
p.Name = "HACKED!";

OOP composition

I have a question regarding OOP composition.
Let's say that a mother has 0 or plus children, and a child has one and only one biologic mother.
To illustrate it, I did the following :
public class Mother : ObservableObject
{
// [...]
ObservableCollection<Child> Children {get; set;}
}
public class Child : ObservableObject
{
public Child(Mother mother)
{
this.Mother = mother;
// Adding the child to the mother's children collection
mother.Children.Add(this);
}
public Mother Mother {get; set;}
}
but I wonder if it's okay to automatically add the child to the mother's collection, or if I should go with the following :
Mother mother = new Mother();
Child child = new Child(mother);
mother.Children.Add(child);
Thanks :)
I'd prefer,
public class Mother : ObservableObject
{
// ...
public Child GiveBirth()
{
var newBorn = new Child(this);
this.Children.Add(newBorn);
return newBorn;
}
// ...
}
I think the modeling is a little off. A Mother and a Child are semantically related to one another, but they're instances of the same object. They are both a Person.
The creation of a Person is an operation performed by a Person. So a Person shouldn't even have a public constructor, but rather a factory method which takes care of this logic. Something like this:
public class Person : ObservableObject
{
private Person()
{
Children = new ObservableCollection<Person>();
}
public Person Mother { get; private set; }
public ObservableCollection<Person> Children { get; private set; }
public Person Procreate()
{
var child = new Person();
child.Mother = this;
this.Children.Add(child);
return child;
}
}
This modeling is still a bit limited, for example we're only talking about asexual reproduction here. So we're not effectively modeling humans yet. Perhaps we need to add a father?
public class Person : ObservableObject
{
private Person()
{
Children = new ObservableCollection<Person>();
}
public Person Mother { get; private set; }
public Person Father { get; private set; }
public ObservableCollection<Person> Children { get; private set; }
public Person Procreate(Person father)
{
var child = new Person();
child.Mother = this;
child.Father = father;
this.Children.Add(child);
father.Children.Add(child);
return child;
}
}
We'll want to add some checking for nulls and whatnot of course. Now we've also discovered that we need to specify genders. (While family structures may vary considerably, the act of creating a person is pretty well established.) So we can keep adding features like that. At some point we may indeed subclass these, but those subclasses will likely end up being mostly semantic pass-through objects with hard-coded default values for this Person superclass.
But just for fun, let's try adding genders...
public class Person : ObservableObject
{
private Person(Sex gender, Person mother, Person father)
{
// TODO: Check for null mother and father
this.Gender = gender;
this.Mother = mother;
this.Father = father;
Children = new ObservableCollection<Person>();
}
public Sex Gender { get; private set; }
public Person Mother { get; private set; }
public Person Father { get; private set; }
public ObservableCollection<Person> Children { get; private set; }
public Person Procreate(Person father)
{
// TODO: Check for null father, confirm gender of father
var child = new Person(PickRandomGender(), this, father);
this.Children.Add(child);
father.Children.Add(child);
return child;
}
private Sex PickRandomGender() { /.../ }
public enum Sex
{
Female,
Male
}
}
Ok, that was fun. Cleaned up a little bit by moving some logic to the constructor as well. But now there's another problem... fathers can procreate. Which sounds kind of painful. Now it looks like we're ready to subclass:
public class Person : ObservableObject
{
protected Person(Sex gender, Person mother, Person father)
{
// TODO: Check for null mother and father
this.Gender = gender;
this.Mother = mother;
this.Father = father;
Children = new ObservableCollection<Person>();
}
public Sex Gender { get; private set; }
public Person Mother { get; private set; }
public Person Father { get; private set; }
public ObservableCollection<Person> Children { get; private set; }
protected Sex PickRandomGender() { /.../ }
public enum Sex
{
Female,
Male
}
}
public class Woman : Person
{
// TODO: Override Gender with a hard-coded value
public Person Procreate(Person father)
{
// TODO: Check for null father, confirm gender of father
var child = new Person(PickRandomGender(), this, father);
this.Children.Add(child);
father.Children.Add(child);
return child;
}
}
(Should we subclass a Man as well? It semantically seems cleaner, but are there any operations or attributes specific to men that aren't shared by women? Perhaps, but our models aren't that detailed yet.)
Looking back, the classes of Mother and Child seem kind of limited and short-sighted at this point. A woman isn't necessarily a mother, and all people are children. As you can imagine, there are plenty of features to add to this system. But following the same general process of building out the domain like this should accommodate that.
Suppose you have a class name building. This class building has a function called BuildRooms(). There is another class Room having functions to make rooms. You create objects of class Room as r1, r2,r3 etc. Now this building has 3 rooms. We can open doors and close doors methods inside Room class. Building class is composed of Rooms. It means this building has 3 rooms. Write code in any language you prefer. This is composition. Building has rooms so it is Has-A relationship.
class building{
void make_rooms()
{
room r1=new room(), r2=new room();
r1.open();
r2.close();
}
}
class room{
void open()
{
}
void close()
{
}
}

Force EF 5 or 6 to map interface member

The question is very simple and direct: What do I have to do to make EF (5 or 6) create the database accordingly to this code
class Program
{
static void Main(string[] args)
{
Person parent = new ResponsablePerson();
parent.Name = "Father";
Person child = new Person();
child.Name = "Child";
child.Parent = parent;
using (PersonContext pc = new PersonContext())
{
pc.Persons.Add(parent);
pc.Persons.Add(child);
pc.SaveChanges();
}
Console.ReadKey();
}
}
public class Person : IPerson
{
[Key]
public string Name { get; set; }
public IPerson Parent { get; set; }
public virtual void Work()
{
Console.WriteLine("How much are you payng me? Ok I'll do it!");
}
}
public class ResponsablePerson : Person
{
public override void Work()
{
Console.WriteLine("Right Now!");
}
}
public class NotResponsablePerson : Person
{
public override void Work()
{
Console.WriteLine("Oh HELL NO!");
}
}
public interface IPerson
{
string Name { get; set; }
IPerson Parent { get; set; }
void Work();
}
The thing is that the database EF creates contains only 1 column for the name of the person...
public class Person : IPerson
{
public virtual Parent Parent { get; set; }
IParent IPerson.Parent
{
get { return this.Parent; }
set
{
if (!(value is Parent)) throw new ArgumentException();
this.Parent = (Parent)value;
}
}
}
As you can see, the trick is to have two properties, one to make EF work (returning type is Parent) and the other to satisfy the interface (returning type is IParent). The trick is possible by implementing the interface in an explicit way.

What is the best way to populate base properties?

I'm wondering if someone can help me with what is the best way to populate the base properties of a derived class. I would like to use one method to populate the properties of the base whether the base or the child is being used.
Here is an example of what I am asking:
public class Parent
{
public string Id {get; set;}
}
public class Child : Parent
{
public string Name {get; set;}
}
public Parent GetParent(int ID)
{
Parent myParent = new Parent();
//Lookup and populate
return Parent;
}
public Child GetChild(string name)
{
Child myChild = new Child();
//Use the GetParent method to populate base items
//and then
//Lookup and populate Child properties
return myChild;
}
I think you might be overcomplicating things a bit. Take a look at this code that uses inheritance and constructors to initialize objects:
public class Parent
{
public string Id {get; set;}
public Parent(string id)
{
Id = id;
}
}
public class Child : Parent
{
public string Name {get; set;}
public Child(string id, string name) : base(id) // <-- call base constructor
{
Name = name;
}
}
It uses constructors for initialization and the base keyword to call the parent constructor from the derived class. I would go this direction unless you really need to have a factory method construct your object.
Something like this if you don't want to do it in constructor.
Note: the constructor is not always called, especially if the type is desirialized using certain serializators.
public class Parent
{
public string Id {get; set;}
public virtual void InitPorperties() {
//init properties of base
}
}
public class Child : Base {
public override void InitProperties() {
//init Child properties
base.InitProperties();
}
}
After this you can use it like:
public Parent GetParent(int ID)
{
var myParent = new Parent();
parent.InitProperties();
return myParent;
}
public Parent GetChild(int ID)
{
var child= new Child();
child.InitProperties();
return child;
}
As anything it has other side of coin: the caller has to call InitProperties method in oder to get correctly initialized object.
If the serialization/desialization is not a concern in your case, stick with constructors, in practice call this methods inside ctors of every type (Parent, Child)
If you dont want to use a standard way to just
Child myChild = new Child();
myChild.Name = "name";
myChild.Id = "1";
You can populate them via the constructor like this.
public class Parent
{
public Parent(string id)
{
Id = id;
}
public string Id { get; set; }
}
public class Child : Parent
{
public Child(string id, string name)
: base(id)
{
name = Name;
}
public string Name { get; set; }
}
And when you isntanciate it
Child myChild = new Child("1", "name");
Which in my opinion is a quite neat way to do it.

Acessing object like by Index, but by name in C#

I have to classes, Father and Child (by example)
A snippet of my implementation
Class Father.cs
public class Father
{
public int Id { get; set; }
public string Name { get; set; }
public List<Child> Children { get; set; }
public Father()
{
}
}
Class Child.cs
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public Child()
{
}
}
I'am trying to do something like this
Father f = new Father();
f.Children[0]; // ok
f.Children[1]; // ok
f.Children["John"]; // Duh!
I now, its wrong, i need to implement something in Child Class, i tryed this
public Child this[string name]
{
get
{
return this;
}
}
But this doesnt work.
How can i implement this feature for my class Child?
A List<T> doesn't have a string indexer; you could add one to the Father class, but the usage will be:
var child = parent["Fred"];
(no .Children)
For the indexer itself: Try (in the indexer):
return Children.FirstOrDefault(c=>c.Name==name);
To get an indexer on the list itself, you would have to create a custom list type and add the indexer there.
IMO, a method may be clearer (on Father):
public Child GetChildByName(string name) {...}
Or you can set it up like this:
public class Father
{
public int Id { get; set; }
public string Name { get; set; }
public Children Children { get; set; }
public Father()
{
}
}
public class Children : List<Child>
{
public Child this[string name]
{
get
{
return this.FirstOrDefault(tTemp => tTemp.Name == name);
}
}
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public Child()
{
}
}
Then you call it how you want.
In father class, you could write following code:
public Child this[string name]
{
get
{
return Children.Where(c => c.Name == name);
}
}
and use it like:
Father f = new Father();
f.Children[0]; // ok
f.Children[1]; // ok
f["John"]; // ok!
You are treating the List of Children like a Dictionary (Dictionaries can be accessed by key). Just change your List to a Dictionary and set the string to be the Child's name.
You could make the Children List into an OrderedDictionary so that you can reference it by index or key and then add the objects with the name as the key. Just so you know though, any of these options can run into issues if you have multiple children with the same name.

Categories

Resources