none
Reflection on complex classes with inheritance and abstraction RRS feed

  • Question

  • Hello,

    public class Machine : SuperToolsClass
    {
        public Machine()
        {
            ToolboxClass = new MiniTool();
        }
    
        internal sealed class MiniTool : ToolBoxAbstract
        {
            private Transporter trans { get; set; }
    
            public MiniTool() : base()
            {
                //some code
            }
    
            public override void Start()
            {
                //some code
            }
    
            
            private class Transporter : SomeotherClass
            {
                //some code
            }
        }
    }
    
    
    
    public class SuperToolsClass
    {
        public ToolBoxAbstract ToolboxClass { get; set; }
    
        public abstract class ToolBoxAbstract
        {
            private Transporter trans { get; set; }
    
            public abstract void Start();
    
            protected ToolBoxAbstract()
            {
                //some code
            }
    
            private abstract class Transporter : SomeotherClass
            {
                protected Transporter() : base()
                {
                }
            }
        }
    }

    I have the code above, Machine is the main class and SuperToolsClass is the one being inherited from Machine.  Like Machine class I have plenty others with the excact same structure/skeleton and inheritances like you see in Machine class.

    So, I load the names (in string format) of all those classes lets say from a database and heres where I want to use reflection.

    I want to instantiate Machine class and call method Start from ToolboxClass object.

    Below the code that tries to do it...

    var className = "Machine";
    var myClass = Type.GetType("mynamespace.etc." + className);
    if (myClass != null && myClass.IsClass && className == "Machine")
    {
        var instance = Activator.CreateInstance(myClass, null);
        var type = instance.GetType();
        var nestedTypes = type.UnderlyingSystemType.GetTypeInfo().DeclaredNestedTypes;
        foreach (var nested in nestedTypes)
        {
            switch (nested.Name)
            {
                case "MiniTool":
                    var members = nested.GetTypeInfo().DeclaredMethods;
                    var connect = members.FirstOrDefault(x => x.Name == "Start");
                    connect?.Invoke(nested, null);
                    break;
            }
        }
    }

    On the Invoke I get the Exception: System.Reflection.TargetException: 'Object does not match target type.'

    Thanks

    Saturday, May 5, 2018 2:15 AM

Answers

  • I'm looking at your code and you're using abstract classes so you may or may not need an interface. You can stick with abstract classes. I would however get rid of the nesting types. Use nested types only when you want to hide implementation details. I also notice you're already exposing the ToolboxClass as a public member so you have everything you need to eliminate the reflection at this point.

    class Program
    {
        static void Main ( string[] args )
        {
            //Can still use reflection if you want but maybe using a DI container may be more efficient
            var className = "Machine";
                
            //Ignoring namespace issues here
            var type = Type.GetType(className);
    
            //Using the default constructor, a DI container would allow you to pass parameters
            var tool = Activator.CreateInstance(type) as SuperTools;
    
            //Start it
            tool.ToolBox?.Start();
        }
    }
    
    public class Machine : SuperTools
    {
        public Machine ()
        {
            ToolBox = new MiniTool();
        }
    }
    
    public class MiniTool : ToolBox
    {
        protected override void StartCore () { }
    }
    
    //Changed the naming here
    //Could use generics here but it makes it harder to work with dynamically
    public abstract class SuperTools
    {
        //Can the toolbox every be null? If not then make the setter private and require the tool in the constructor
        public ToolBox ToolBox { get; protected set; }
    }
    
    //Changed the naming here
    public abstract class ToolBox
    {
        //I tend to make my abstract members protected
        public void Start ()
        {
            StartCore();
        }
    
        protected abstract void StartCore ();
    }

    As mentioned in the code, if each tool has to have a toolbox then make the setter private, require the abstract class constructor to accept it.

    Switching to a DI container would eliminate some of the code needed to create the instance and would allow you to support passing parameters to the tool but I'd start with the above.

    If you want to introduce interfaces then define the interface against the abstract SuperTools (and Toolbox) class. VS 2017 has a quick action that will do this for you.

    You could also make SuperTools generic such that Toolbox would be strongly typed but since you're dynamically creating instances via reflection this would complicate the reflection logic quite a bit.


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, May 23, 2018 2:11 PM
    Moderator

All replies

  • Hello Christose88,

    Because "start" is instance method rather than static method, when you use reflect to invoke instance method you must create instance first as below code.

    ...

    case "MiniTool": var instan = (Machine.MiniTool)Activator.CreateInstance(nested); var connect = nested.GetTypeInfo().DeclaredMethods.FirstOrDefault(x => x.Name == "Start"); connect?.Invoke(instan, null); break;

    ...


    And remove useless code as below(create valueless instance)

       var className = "Machine";
                var myClass = Type.GetType("OP_Test." + className);
                if (myClass != null && myClass.IsClass && className == "Machine")
                {
                    var nestedTypes = myClass.UnderlyingSystemType.GetTypeInfo().DeclaredNestedTypes;
                    foreach (var nested in nestedTypes)
                    {
                        switch (nested.Name)
                        {
                            case "MiniTool":
                                var instan = (Machine.MiniTool)Activator.CreateInstance(nested);
                                var connect = nested.GetTypeInfo().DeclaredMethods.FirstOrDefault(x => x.Name == "Start");                         
                                connect?.Invoke(instan, null);
                                break;
                        }
                    }
                }

    Best Regards,

    Neil Hu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, May 7, 2018 2:52 AM
    Moderator
  • Hello Neil Hu,

    Thanks for you answer and the helpful correction regarding Start, I missed that part.

    However, the thing is that Machine will come from a database as a string and like Machine I'm going to have 100+ different names/classes, so I don't want to have a switch with 100+ cases, that's why I was trying to avoid such a switch with the use of reflection.  So I want to call Connect of MiniTool of each and one of those 100+ classes like Machine.

    Any idea that comes to your mind ?

    Tuesday, May 22, 2018 1:40 PM
  • I would recommend you wrap this stuff in an interface. Then you can use Activator.CreateInstance to create the concrete type and then use the interface from there without reflection. Additionally the nested type should either be completely managed by the parent type or it shouldn't be a nested type at all. Then you can access the nested type instance either by interacting with the parent directly or by exposing a public property to get the instance associated with the parent. Either way you're avoiding reflection and getting compile time checking.

    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, May 22, 2018 2:01 PM
    Moderator
  • Thanks.

    Do you have time to show me a little bit your idea with some code and at the same time I will prepare my idea, how I was thinking to implement it at the beginning with the interface you said and some abstract, because I want also all these classes being saved in a list for future calls and interface is quite necessary if the reflection is not on the table.

    Wednesday, May 23, 2018 9:47 AM
  • I'm looking at your code and you're using abstract classes so you may or may not need an interface. You can stick with abstract classes. I would however get rid of the nesting types. Use nested types only when you want to hide implementation details. I also notice you're already exposing the ToolboxClass as a public member so you have everything you need to eliminate the reflection at this point.

    class Program
    {
        static void Main ( string[] args )
        {
            //Can still use reflection if you want but maybe using a DI container may be more efficient
            var className = "Machine";
                
            //Ignoring namespace issues here
            var type = Type.GetType(className);
    
            //Using the default constructor, a DI container would allow you to pass parameters
            var tool = Activator.CreateInstance(type) as SuperTools;
    
            //Start it
            tool.ToolBox?.Start();
        }
    }
    
    public class Machine : SuperTools
    {
        public Machine ()
        {
            ToolBox = new MiniTool();
        }
    }
    
    public class MiniTool : ToolBox
    {
        protected override void StartCore () { }
    }
    
    //Changed the naming here
    //Could use generics here but it makes it harder to work with dynamically
    public abstract class SuperTools
    {
        //Can the toolbox every be null? If not then make the setter private and require the tool in the constructor
        public ToolBox ToolBox { get; protected set; }
    }
    
    //Changed the naming here
    public abstract class ToolBox
    {
        //I tend to make my abstract members protected
        public void Start ()
        {
            StartCore();
        }
    
        protected abstract void StartCore ();
    }

    As mentioned in the code, if each tool has to have a toolbox then make the setter private, require the abstract class constructor to accept it.

    Switching to a DI container would eliminate some of the code needed to create the instance and would allow you to support passing parameters to the tool but I'd start with the above.

    If you want to introduce interfaces then define the interface against the abstract SuperTools (and Toolbox) class. VS 2017 has a quick action that will do this for you.

    You could also make SuperTools generic such that Toolbox would be strongly typed but since you're dynamically creating instances via reflection this would complicate the reflection logic quite a bit.


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, May 23, 2018 2:11 PM
    Moderator