0

Having multiple classes doing many things, I am obliged to instantiate one of them, populate some properties and call a method. A sample would be having the below methods e.g.

        public class Method100Response201
    {
        public string R1_01 { get; set; }
        public void DoSpecialThing()
        { Console.WriteLine ("Something Blue..}"); }
    }
    public class Method100Response404
    {
        public string R2_01 { get; set; }
        public void DoSpecialThing()
        { Console.WriteLine ("Something Old..}"); }
    }
    public class Method110Response200
    {
        public string R3_01 { get; set; }
        public void DoSpecialThing()
        { Console.WriteLine ("Something New..}"); }
    }

all of them inside Program class in the same namespace, where I have the mechanism to find out which Class I need:

       static void Main(string[] args)
    {
        int[] MethodResponse = DoSomethingHere (23, "something", true);
        string DerivedClassName = ResponseModel(MethodResponse[0], MethodResponse[1]);
        Console.WriteLine (
            "For method " + MethodResponse[0].ToString () 
            + " and response " + MethodResponse[1].ToString ()
            + " you must instantiate Class " 
            + DerivedClassName);
        Console.ReadKey ();

        //how do I do this????
        //const string objectToInstantiate = "MyProject.Domain.MyNewTestClass, MyTestProject";
        //var objectType = Type.GetType (objectToInstantiate);
        //dynamic instantiatedObject = Activator.CreateInstance (objectType) as ITestClass;
        // set a property value
        //instantiatedObject.Name = DerivedClassName;
        // get a property value
        //string name = instantiatedObject.Name;

        // call a method - output "Something Blue.."
        //Console.Write (instantiatedObject.DoSpecialThing ());

    }

    public static int[] DoSomethingHere (int param1, string param2, bool whatever)
    {
        int firstInt = 0; int secondInt = 0;
        //
        //lots of work here, I end up with method and selector.. 
        //
        firstInt = 100;
        secondInt = 201;
        return new int[] { firstInt, secondInt };
    }

    public static string ResponseModel(int method, int response)
    {
        string returnClass = String.Empty;

        switch (method)
            {
            case 100:
                if (response == 201)
                { Console.WriteLine ("Case 100,201"); returnClass = "Method100Response201"; }
                else
                { Console.WriteLine ("Case 100,404"); returnClass = "Method100Response404"; }
                break;
            case 110:
                Console.WriteLine ("Case 100,404"); returnClass = "Method110Response200";
                break;
            case 120:
                // ...
                break;
            }
        return returnClass;
    }

I tried with something called Activator, I am no expert and this is really critical for me. Could someone please help me? (I left commented on some code I am trying to, based on published solutions here in SO. Thank you).

Vishal Parmar
  • 524
  • 7
  • 27
Nick
  • 483
  • 1
  • 6
  • 15
  • see if this helps https://stackoverflow.com/questions/648160/how-do-i-create-an-instance-from-a-string-in-c – Alex Feb 07 '20 at 03:49
  • https://stackoverflow.com/q/2247598/1070452 – Ňɏssa Pøngjǣrdenlarp Feb 07 '20 at 03:50
  • Does this answer your question? [How do I instantiate a class given its string name?](https://stackoverflow.com/questions/2247598/how-do-i-instantiate-a-class-given-its-string-name) – devNull Feb 07 '20 at 03:51
  • So initiating a class from a string is just bad practice and prone to errors. It would be better to store the class name string and the class instance in a dictionary and then lookup the instance based on the class name string. – jgetner Feb 07 '20 at 03:57

2 Answers2

2

This technical called Reflection, that means call an instance from string. My calling class will be

public class Class1
{
    public string Property { get; set; } = "I'm class1";
    public void DoSpecialThings()
    {
        Console.WriteLine("Class1 does special things");
    }
}

Next I create an instance in a static function, should put your all classes in a same namespace to easy control

    public static dynamic GetClassFromString(string className)
    {
        var classAddress = $"NetCoreScripts.{className}";
        Type type = GetType(classAddress);

        // Check whether the class is existed?
        if (type == null)
            return null;

        // Then create an instance
        object instance = Activator.CreateInstance(type);

        return instance;
    }

And a GetType method

    public static Type GetType(string strFullyQualifiedName)
    {
        Type type = Type.GetType(strFullyQualifiedName);
        if (type != null)
            return type;
        foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
        {
            type = asm.GetType(strFullyQualifiedName);
            if (type != null)
                return type;
        }
        return null;
    }

I use dynamic type to implement quickly, basically you can use interface for explicit coding.

    static async Task Main(string[] args)
    {
        dynamic instance = GetClassFromString("Class1");

        Console.WriteLine(instance.GetType().FullName); //NetCoreScripts.Class1

        Console.WriteLine(instance.GetType().Name); //Class1

        Console.WriteLine(instance.Property); //I'm class1

        instance.Property = "Class1 has been changed";
        Console.WriteLine(instance.Property); //Class1 has been changed

        instance.DoSpecialThings(); // Class1 does special things
    }
Tấn Nguyên
  • 1,607
  • 4
  • 15
  • 25
  • Thank you for taking time into it, I am looking into your solution trying to make it fit to the working example.. – Nick Feb 07 '20 at 08:41
  • May I ask, how do I set a value for a known Property of class1 in the last box of code? Like class1.MyKnownProperty = 123; ? – Nick Feb 07 '20 at 09:48
  • I got `Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Cannot perform runtime binding on a null reference'` inside the task, after declaring dynamic instance (in the Console.WriteLine). Any advice please? – Nick Feb 08 '20 at 15:25
  • Fixed it, thank you very much. Only small thing now is how to make it lookup in multiple namespaces, unfortunately I have mane (instead of just the $"NetCoreScripts.{className}" for example). Thank you very much for your contribution, appreciated. – Nick Feb 09 '20 at 00:09
  • @Nick You are welcome, about list all classes in a namespace, this answer may help you https://stackoverflow.com/a/949285/3789481 – Tấn Nguyên Feb 11 '20 at 05:00
0

The problem that you are trying to solve is with regard Creation of new objects. Here are some common coding patterns used for the same.

I would say this is a good use case for Factory pattern. What we do is delegate the responsibility for creating objects to a factory.

Firstly, we can identify them as classes that extend an abstract class

public abstract class MehodResponseBase {
    public abstract void  DoSpecialThing();
    public abstract string responseText;
}

public class Method100Response201 : MehodResponseBase
{
    public override responseText = "Method100Response201";
    public string R1_01 { get; set; }
    public override void DoSpecialThing()
    { Console.WriteLine ("Something Blue..}"); }
}
public class Method100Response404 : MehodResponseBase
{
    public override responseText = "Method100Response404";

    public string R2_01 { get; set; }
    public override void DoSpecialThing()
    { Console.WriteLine ("Something Old..}"); }
}
public class Method110Response200 : MehodResponseBase
{
    public override responseText = "Method110Response200";

    public string R3_01 { get; set; }
    public override void DoSpecialThing()
    { Console.WriteLine ("Something New..}"); }
}

Then we can extract their creation into a factory

pubic class MethodResponseFactory() 
{
    public static MehodResponseBase Make(int method, int response)
    {
        if (method == 100) 
        {
            if(response == 201) 
            {
                return new Method100Response201();
            }

            if(response == 404) 
            {
                return new Method100Response404();
            }
        }

        if (method == 110) 
        {
            if (response == 200) 
            {
                return new Method110Response200();
            }
        }

        throw new MethodResponseCreationException($"Cannot create for mothod: {method} and response: {response}")
    }

}

So your response model is refactored to

public static string ResponseModel(int method, int response)
{
        try 
        {
            return MethodResponseFactory.Make(method, response).returnClass;
        }
        catch (MethodResponseCreationException ex)
        {
            return string.Empty;
        }
}

So, as you can see, all the delegation for creating the object is now in the factory. And resonseModel class simply calls the factory to build a class based on the method and response.

Tejas Parnerkar
  • 201
  • 1
  • 5
  • Thank you very much for your answer, I appreciate, but I was looking forward for a solution with Activator (reason is I have many classes and not too many common point - although they have some - to create abstraction), sorry if the example oversimplified it.. – Nick Feb 07 '20 at 08:38