I just try to redesign my Silverlight-4 App and tried a bit around with generics.
Simply speaking, I have a tree which can contain 2 types of nodes. As a base class, I created a class that does all the "organization", like having a list of children, a parent, a method to add a child and so on:
public abstract class BaseNode<T> : INotifyPropertyChanged where T: BaseNode<T>
{
protected ObservableCollection<T> _children;
...
}
Second, I add a class that inherits from BaseNode and is the basis for all my treenodes:
public class ServiceNodeBase<T> : BaseNode<ServiceNodeBase<T>> where T : ServiceNodeBase<T>
{
public string Description { get; set; }
...
}
And finally, as I can have two different kinds of nodes, I create a class for each kind, i.e.:
public class ServiceNodeComponent<T> : ServiceNodeBase<ServiceNodeComponent<T>> where T : ServiceNodeComponent<T>
{
public HashSet<Attributes> Attributes { get; set; }
...
}
In the ServiceNodeComponent, I need a method, that scans the tree i.e. to get all the child nodes, that are of the Type ServiceNodeComponent. When parsing the tree, I need to use the parent-type of ServiceNodeComponent (ServiceNodeBase), because the child nodes can also be of the other type.
Now, I do not know how to instantiate the ServiceNodeBase-Variable.
public HashSet<ServiceNodeComponent<T>> GetAllChildComponents()
{
// declaring the container for the found Components is no problem
HashSet<ServiceNodeComponent<T>> resultList = new HashSet<ServiceNodeComponent<T>>();
// but now: how to declare the container for the ServiceBaseNodes?
HashSet<ServiceNodeBase<???>> workingList = new HashSet<ServiceNodeBase<???>>();
Any ideas, how I would implement this?
Thanks in advance,
Frank
The problem is the constraint. It would work if you change it to
public class ServiceNodeComponent<T> : ServiceNodeBase<ServiceNodeComponent<T>>
where T : ServiceNodeBase<T> {
public HashSet<ServiceNodeComponent<T>> GetAllChildComponents() {
// ...
HashSet<ServiceNodeBase<T>> workingList = new HashSet<ServiceNodeBase<T>>();
// ...
}
}
Related
I am trying to insolate classes from others setting or changing data in the class. I have chosen to use an abstract base class called Parent and then two derived abstract classes called DerivedA and DerivedB. Then, using Assembly, I get the derived abstract classes from Parent. Then, I use generics to derive a concrete class, ConcreteGeneric, to hopefully fill in the values of the abstract classes .
The problem I am having is that when I get into my concrete class, I do not have access (see) the parent class members/properties. Maybe this design is all wrong, but this is the ideal way I would like to solve it. Any help would be greatly appreciated... and save the hair that is falling off my head. ;)
Code is attached.
I have documented what I would like in the code. To be able to access and see the public variables in the parent classes.
using System;
using System.Linq;
using System.Reflection;
public abstract class Parent
{
public string Name { get; set; }
public string Comment { get; set; }
}
public abstract class DerivedA : Parent
{
public string DerivedAString { get; set; }
}
public abstract class DerivedB : Parent
{
public string DerivedBString { get; set; }
}
public class DerivedFromA : DerivedA
{
public string DerivedFromAString { get; set; }
}
public class ConcreteGeneric<T> where T : Parent
{
private string _jsonString = "";
public string HeaderString
{
get
{
return _jsonString;
}
set
{
/// I want to be able to see the Derived classes parameters
/// here. Like 'DerivedB.DerivedBString' if T is type DerivedB
_jsonString = value;
}
}
}
public class RunItClass
{
public static void Main()
{
Type[] types = Assembly.GetAssembly(typeof(Parent)).GetTypes();
foreach (Type type in Assembly.GetAssembly(typeof(Parent)).GetTypes()
.Where(myType => myType.IsAbstract && myType.IsSubclassOf(typeof(Parent))))
{
var genType = typeof(ConcreteGeneric<>).MakeGenericType(type);
Type genericType = (Type)genType;
object genericInstance = Activator.CreateInstance(genericType);
dynamic dynamicObj = Convert.ChangeType(genericInstance, genericType);
/// Note that when I drop into the 'set' method on this dynamic object, I cannot see the
/// paramters of the parent class, which is 'DerivedA' on the first item in this loop.
dynamicObj.HeaderString = "Testing";
// Testing here
if (genericType == typeof(ConcreteGeneric<DerivedA>))
{
// ?? I CANNOT see all of the variables in 'DerivedA' ??
ConcreteGeneric<DerivedA> da = (ConcreteGeneric<DerivedA>)genericInstance;
/// I CAN see all of the variables in 'DerivedA' and also 'Parent'. This is what I
/// am after, but I want to be able to use the ConcreteGeneric<![CDATA[>]]> to accomplish this.
/// Please help. :)
DerivedFromA dfa = new DerivedFromA();
Console.WriteLine();
}
}
}
}
The code inside your ConcreteGeneric<T> class has to work with any T that you might decide to give it. Since you've constrained T to Parent that means you can access any of Parent's properties.
You can say "I want to be able to see the Derived classes parameters here", but what if you created a ConcreteGeneric<DerivedA>? Then there wouldn't be any DerivedBString property there for you to access.
What you might be able to do is to expose your T directly inside ConcreteGeneric<T>:
public T Item { get; }
Then you'll be able to cast your genericType to a ConcreteGeneric<DerivedA>, and access .Item:
var concreteDerivedA = (ConcreteGeneric<DerivedA>)genericType;
string s = conceteDerivedA.Item.DerivedAString;
That leaves you with the question of how Item is set. If you enforce that it must have a parameterless constructor, you can do this:
public class ConcreteGeneric<T> where T : Parent, new()
{
public T Item { get; } = new T();
}
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 one parent class and one child class.
The parent has a constructor that initializes its parameters.
My question is: How does the child look to the parent's constructor? Can I define a constructor for the children?
you can use base(...) in ctor of your child class.
foreacmple:
public class Child : BaseClass
{
public Child() : base(/*some parameters*/) //CALLING BaseClass parametrized ctor
{
}
}
Just note, if you don't need some specific parameters, just do not do anything, cause BaseClass default ctor will be called by the way when you call ctor of a Child class.
This inheritance sample shows:
how to call the parent constructor from a new constructor on the child
how to pass parameters required by the parent constructor
Code sample:
public class Parent
{
private object _member;
public Parent(object member)
{
this._member = member;
}
}
public class Child : Parent
{
public Child(object member)
: base(member)
{
}
}
You have to define constructors for the children. You can call the base class' constructor using : base() between the constructor prototype and its implementation:
public class Parent {
public Parent() {
...
}
}
public class Child : Parent {
public Child() : base() { // calls Parent.ctor
}
}
Of course.
You are after the "base" keyword.
public class Fruit
{
string TypeOfFruit { get; set; }
public Fruit (string typeOfFruit)
{
TypeOfFruit = typeOfFruit;
}
}
public class Apple : Fruit
{
string AppleType { get; set; }
public Apple(string appleType) : base("Apple")
{
AppleType = appleType;
}
}
You can very well define a constructor for the child class the default one is provided only in case when you do not define a constructor for a class
Meanwhile for how to look up for the constructor of parent
It would check for a parameterless constructor to be present in the parent class and in case you do not have one ( well the compiler lets you know the same) or else you will have to call the parent constructor with the parameters like base("This is the string parameter")
If you meant something else please update the question.
I've got a semi-complex inheritance structure that I'm having difficulty with on overriding a constructor on a base class. The following code can show the error:
public abstract class MyBaseObject
{
public MyBaseObject(MyBaseCollection<MyBaseObject> parent)
{
this.Parent = parent;
}
public MyBaseCollection<MyBaseObject> Parent { get; set; }
}
public abstract class MyBaseCollection<T>
where T : MyBaseObject
{ }
public class MyRealObject : MyBaseObject
{
public MyRealObject(MyRealCollection parent)
: base(parent)
{ }
public new MyRealCollection Parent { get { return (MyRealCollection)base.Parent; } }
}
public class MyRealCollection : MyBaseCollection<MyRealObject>
{ }
So, specifically, I can't override the constructor in the MyBaseObject class. Trying to pass in MyRealCollection in place of MyBaseCollection isn't acceptable. If I get rid of the generics arguments, it works; MyRealCollection is accepted in place of MyBaseCollection. But I really need the generics argument to make my collection classes work the way I need them to.
I suggest you look into contravariance and covariance. Here might be a good start http://msdn.microsoft.com/en-us/library/dd799517.aspx
In short, the CLR can't assume what you want it to assume with respect to the type inheritance for some very well-defined reasons that are way above my pay grade.
However, you can do something like this if you play with the type hierarchy a little. I used IEnumerable to help.
public abstract class MyBaseObject
{
public MyBaseObject(IEnumerable<MyBaseObject> parent)
{
this.Parent = parent;
}
public IEnumerable<MyBaseObject> Parent { get; set; }
}
public class MyRealObject : MyBaseObject
{
public MyRealObject(MyRealCollection parent)
: base(parent)
{ }
public new MyRealCollection Parent { get { return (MyRealCollection)base.Parent; } }
}
public class MyRealCollection : IEnumerable<MyRealObject>
{ }
MyRealCollection is not accepted in the place of MyBaseCollection because it is a collection of MyRealObject, not MyBaseObject. For an idea of why, imagine if the constructor for MyBaseObject did this:
public MyBaseObject(MyBaseCollection<MyBaseObject> parent)
{
this.Parent = parent;
parent.Add(new SomeOtherRealObject());
}
That would be perfectly legal from the perspective of MyBaseObject, but not if you had passed in an instance of MyRealCollection
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>
{
}