Jada
Jada

Reputation: 25

Recursively instantiating the classes inside an object

I want to activate, via reflection automatically and recursively all the nested classes inside a class I am instantiating. Here is the template of what I am trying to do, but I do not know how to make the function. I know how to enumerate the members and create the instances, but then I do not know how to assign them to the inner subtypes. Here follows the scheme. I need to do that by reflecting, without making any changes inside the original class. Just activation of the objects inside, from outside. No code rewriting inside the class or adding various "new" inside the class, obviously

        void someCode()
    {
        Nest MyNest = new Nest();
        createInstancesOfAllClassesInside(MyNest);
    }

    void createInstancesOfAllClassesInside(object anyObj)
    {
        //How, using reflection ?
    }

    public class Nest
    {
        public class A
        {
            public class A1
            {
                public class A2
                {
                    public string x = "Activate me!";
                }
            }
        }
        public class B
        {
            public class B1
            {
            }
        }
        public string s = "Hello";
    }

To respond to comments: by activation, I mean "like if" the inner classes had been instantiated by a new(). Thank you!

I will add my (nonworking) attempt to far so it may make the problem clearer:

public void CreateInstancesOfAllClassesInside(object obj)
{
    var objType = obj.GetType();

    // Loop through all nested types inside the object's type
    foreach (Type nestedType in objType.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))
    {

        // Create an instance of the nested type
        var instance = Activator.CreateInstance(nestedType);

        // cant find something like: SetValue(obj, instance) to attach the object to its container !

        // Recursively call the function to instantiate nested classes inside this instance
        CreateInstancesOfAllClassesInside(instance);
    }

}


public void CreateInstancesOfAllClassesInside2(ref object obj)
{

    var objType = obj.GetType();

    // Loop through all nested types inside the object's type
    foreach (Type nestedType in objType.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))
    {
        // Create an instance of the nested type using its constructor
        var instance = Activator.CreateInstance(nestedType);

        // Recursively instantiate any further nested classes inside this instance
        CreateInstancesOfAllClassesInside2(ref instance);

        // Now invoke the constructor of the nested type to ensure proper initialization
        ConstructorInfo[] ctors = nestedType.GetConstructors();
        if (ctors.Length > 0)
        {
            ctors[0].Invoke(instance, (object[])null); // Call the constructor
        }

        // instances of nested classes do not appear in the object root!  :-(

    }

}

I created the instances but could not find a way to "SetValue", that is, to assign the generated instance (via activator) to the corresponding member.

Here is an example of a class that could need that treatment:

 public class _CONST
{
    public class FOLDERS_APP
    {
        public class POSTS
        {
            public const string FOLDER_POSTS_USERS = "somefolder1/";
            public const string FOLDER_PRO_POSTS_FROM_USERS = "somefolder2/";
            public const string FOLDER_FOR_IM_IN_POST = "somefolder3/";
            public const string FOLDER_ATTACH_TO_POSTS = "somefolder4/";
        }
        public class MANIFESTOS
        {
            public const string FOLDER_APPLICATION_MO = "somefolder5/";
            public const string FOLDER_FOM_S = "somefolder6/";
        }
    }
}

In this case, when instantiating the main class, the inner classes are not instantiated. My goal is to instantiate them too [without editing the class and adding instantiators] because I need them for the serialization process. Or else the serializer will not consider them (they would be skipped). That is my constants would not be sent to the client, only those on the outer class (say the first level of the constants I am sending).

Upvotes: -1

Views: 115

Answers (2)

D Stanley
D Stanley

Reputation: 152626

I think you have a misunderstanding of how the classes you describe actaully work.

When you do

public class A {
    public class B {
    }
}

If you create an A object, it does not "contain" a B object property (in only has a type defined within its namespace), so there is no property to assign a new object to.

If you want A to actually _containaB` then you have to add a property:

public class A {
    public class B {
    }

    public B B {get; set;} = new B(); // for example only; using the same property name as the type will cause problems
}

Note that the B property is assigned a B object at initialization. You could use reflection externally to see that A has a B property and assign it a new B, but you can't do that with your current structure because the classes does not have properties that can be assigned, only nested types.

Upvotes: 4

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112632

Nesting the classes does not automatically nest the corresponding objects. Nesting the classes only affects how you can access a class. E.g. if you have two nested classes like this:

public class A
{
    public class B
    {
    }
}

...then you can instantiate class B like this: new A.B(). I.e., this nesting works similar to a nesting inside a namespace. It nests the types but it does not nest the objects.

What you need are nesting through properties:

public class ClassA
{
    public ClassB B { get; set; } = new ClassB();
}

public class ClassB
{
    public ClassC C { get; set; } = new ClassC();
}

public class ClassC
{
    public int Value { get; set; } = 42;
}

Creating a ClassA object will now automatically create a nested ClassB object having a nested ClassC object.

Now you can write this:

var a = new ClassA();
int value = a.B.C.Value; // ==> 42

You can also use static classes to store constants. Then there is no class to be instantiated.

public static class CONST
{
    public static class FOLDERS_APP
    {
        public static class POSTS
        {
            public static readonly string[] FOLDER_POSTS_USERS = ["Tim", "Sue"];
        }
    }
}

Upvotes: 2

Related Questions