Changing the Type of a inherited property (to a inherited type)
Asked Answered
C

4

11

using C# I have a class which contains among other meta information the root node of a directed graph. Let's call this the Container-Class. This container can appear in two different modes, Editor-Mode and Configurator-Mode. Depending on the mode, the root-node is of a different type NodeEdit or NodeConfig, both inheriting from the same subclass.

public abstract class NodeBase
{
  string Name { get; set; }
  ...
}

public class NodeEdit : NodeBase ...
public class NodeConfig : NodeBase ...

For the container, I also create a base class and inherit from it:

public abstract class ContainerBase
{
  NodeBase Root { get; set; }
  ...
}

When creating the classes for Editor- and Configuratorcontainer by inheriting from ContainerBase, I want to become the type of the Root - property the specific (inherited from NodeBase) type like:

public class ContainerEditor : ContainerBase
{
  NodeEditor Root { get; set; }
  ...
}

But I cannot change the type of a property defined in ContainerBase. Is there a way to solve this problem? I can use the BaseNode-type, and add an element of NodeEditor like

ContainerEditorInstance.Root = new NodeEditor();

because the type NodeEditor is inherited from type BaseEditor, but in the Container-Editor class, I want to explicitly only allow the type of the Root-property to be NodeEditor. I could check this in the setter and reject all nodes but those of type NodeEditor, but I'd like to have the property be of the specific type, so I can detect wrong assignments at compile-time.

Thanks in advance,
Frank

Cubeb answered 4/11, 2011 at 10:35 Comment(0)
J
10

You can redeclare it:

public class ContainerEditor : ContainerBase
{
  public NodeEditor Root {
    get { return (NodeEditor)base.Root; }
    set { base.Root = value; }
  }
  ...
}
Jerroldjerroll answered 4/11, 2011 at 10:59 Comment(3)
Hi Marc, thanks for the reply! I already had tried it with redeclaration, but probably I did something wrong. I tested your approach and it works fine! Generally, are there situations where redeclaring should be preferred to Generics or vica versa or are they equal?Cubeb
This whole Generic stuff is causing serious problems when the generic base class has to be used i.e. as a abstract returntype. So I'll give the redeclaring a shot, for it seems to be less intrusive. In comparison to 'new' and 'override', how is redeclaring working internally? Is there any good article about it you can recommend? Thanks again!Cubeb
Reviving this old question to say that this may be a very simple solution but it is easy to overlook and by far the best solution. After trying generics, abstract and virtual classes, etc., this was by far the most elegant way to solve having the same problem as the original question. This allows for the use of the NodeBase class (i.e. a function that takes the class and operates on any type of its children) without any mess.Gasp
T
13

Use generics:

public abstract class ContainerBase<T> where T:NodeBase
{
  T Root { get; set; }
  ...
}

public class ContainerEditor : ContainerBase<NodeEditor>
{      
  ...
}
Tinder answered 4/11, 2011 at 10:40 Comment(2)
Hi Blau, never dealt with generics beside the usual HashSet<T> and similar usage. Think this probably will be a good time working with them!Cubeb
For anyone considering this approach, this will be a pain point if you need to perform operations on all ContainerBase<T>, or treat them as a singular return type.Clangor
J
10

You can redeclare it:

public class ContainerEditor : ContainerBase
{
  public NodeEditor Root {
    get { return (NodeEditor)base.Root; }
    set { base.Root = value; }
  }
  ...
}
Jerroldjerroll answered 4/11, 2011 at 10:59 Comment(3)
Hi Marc, thanks for the reply! I already had tried it with redeclaration, but probably I did something wrong. I tested your approach and it works fine! Generally, are there situations where redeclaring should be preferred to Generics or vica versa or are they equal?Cubeb
This whole Generic stuff is causing serious problems when the generic base class has to be used i.e. as a abstract returntype. So I'll give the redeclaring a shot, for it seems to be less intrusive. In comparison to 'new' and 'override', how is redeclaring working internally? Is there any good article about it you can recommend? Thanks again!Cubeb
Reviving this old question to say that this may be a very simple solution but it is easy to overlook and by far the best solution. After trying generics, abstract and virtual classes, etc., this was by far the most elegant way to solve having the same problem as the original question. This allows for the use of the NodeBase class (i.e. a function that takes the class and operates on any type of its children) without any mess.Gasp
P
1

You can make the container base generic:

public abstract class ContainerBase<TRoot> where TRoot : NodeBase
{
  TRoot Root { get; set; }
  ...
}

In the derived class you specify the type:

public class ContainerEditor : ContainerBase<NodeEditor>
{
  ...
}
Prophecy answered 4/11, 2011 at 10:41 Comment(0)
T
0

I suppose a good solution here will be Generics. So you'd write something like this:

public class ContainerEditor<T>:ContainerBase
{
    T Root {get;set;}
}
Turino answered 4/11, 2011 at 10:42 Comment(1)
I would need to declare the Root in the ContainerBase to be able to access it in functions that doesn't need to know wheter its the Editor or Configurator. Anyway generics sounds like a good idea, like Guffa and Blau showed.Cubeb

© 2022 - 2024 — McMap. All rights reserved.