Chris
Chris

Reputation: 365

Derived Generic Class Doesn't See Parent's Variables

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();
            }
        }
    }
}

Upvotes: 0

Views: 165

Answers (1)

canton7
canton7

Reputation: 42360

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();
}

Upvotes: 1

Related Questions