Choose a class from string value

I have 3 C# classes: a_to_b, b_to_c and a_to_c (each one has a method print() that prints “a to b”, “b to c”, “a to c”).

In another class, I have 2 strings class_1 and class_2 that will be “a”, “b” or “c”.

Given those 2 strings (eg.: “b” and “c”), how can I choose the correct code to call the method print (in this case, it would be b_to_c.print() ) from?

PS: I can’t handle this with if’s as this 3 classes are just an example, I can have 1000 classes X_to_Y that can be chosen by 2 strings.

this is a magic pseudo-code for what I want:
3093338--233391--upload_2017-6-2_21-45-54.png

I thought about every class x_to_y inherit from some superclass (for example, this CorrectClass could be a class from which every x_to_y inherit from) but I still didn’t come up with something…

Do all classes inherit from the same base class with a virtual print() function? If so, then it’s just a matter of choosing the right instance from a dictionary based on the string values.

Or do you need to create an instance of a class based on the string values? Is that your real question? If so, try using Activator.CreateInstance like so:

var type = Type.GetType("MyFullyQualifiedTypeName");
var myObject = (MyAbstractClass)Activator.CreateInstance(type);

The second option, I need to create an instance of a class based on the string values… This code is not very clear for me yet… for example, supposing there is no inheritance, if my strings are “a” and “b”, then I would do

var type = Type.GetType("ab");
var myObject = Activator.CreateInstance(type);

?
or do I need a class for all classes x_to_y to inherit from in order to do this?

It’d be a lot easier if they all had a common base class. Otherwise how do you know they all have a print() function? It’d be easier to enforce that all classes must have a print() function.

Otherwise, you’re looking at more reflection fun to first check if the object contains a method you’re looking for before you can safely invoke it.

EDIT: If you really want to do this, then here’s how to do it in a nutshell:

Type type = typeof("SomeClass");
MethodInfo methodInfo = type.GetMethod("print");

if (methodInfo != null)
{
  var classInstance = Activator.CreateInstance(type, null);
  var parametersArray = new object[] { "Hello" };
  var result = methodInfo.Invoke(classInstance, parametersArray);
}

Disclaimer: I haven’t tested if this compiles.

For starters, you need each of your classes to be polymorphic with one another. Either the same base class (like BlackPete suggests) or they each must implement the same interface. I personally pick the interface route:

public interface IPrintable
{
    void Print();
}

public class A_To_B : IPrintable
{
    public void Print()
    {
        //do stuff
    }
}

public class B_To_C : IPrintable
{
    public void Print()
    {
        //do stuff
    }
}

public class A_To_C : IPrintable
{
    public void Print()
    {
        //do stuff
    }
}

Next you have a combination where you have 2 parameters. You want to combine them to get the class.

Ok, we can use reflection for this… but you need to make sure you format the string correctly:

class_1 = "a";
class_2 = "b";
string className = class_1.ToUpper() + "_To_" + class_2.ToUpper();

Note here I made sure that the letters are uppercase, and that the result will spell out the class correctly.

Though honestly… I don’t like this because what if someone buts in “B”, “A”… results in “B_To_A”… a class that doesn’t exist.

Or what stops them from putting in D-Z?

You only have 3 classes here… so why not create an enum for the valid classes:

public enum ClassOptions
{
    A_To_B,
    B_To_C,
    A_To_C
}

Then you can retrieve instances like so:

//note it returns IPrintable, this is the polymorphic bit
public IPrintable GetObject(ClassOptions option)
{
    switch(option)
    {
        case ClassOptions.A_To_B:
            return new A_To_B();
        case ClassOptions.B_To_C:
            return new B_To_C();
        case ClassOptions.A_To_C:
            return new A_To_C();
        default:
            return null;
    }
}

Used:

public class ClassChooser
{
   
    public ClassOptions option;
   
    public void Print()
    {
        IPrintable obj = GetObject(option);
        if(obj != null) obj.Print();
    }
   
}

Of course if you don’t want to have to keep adding new entries to this enum… what I use is a special type I call ‘TypeReference’:

And its PropertyDrawer:

This is a far more complex setup, but basically what’s going on is I draw a dropdown of any types (the ConfigAttribute allows restricting the type’s allowed), serializing it as a string name, and then at runtime retrieving the type.

Then using Activator.CreateInsance, you can instantiate them.

Here’s a basic example of me using it to add components to GameObjects:

#pragma warning disable 0649 // variable declared but not used.

using UnityEngine;

using com.spacepuppy.Utils;

namespace com.spacepuppy.Scenario
{
    public class i_AddComponent : TriggerableMechanism
    {

        #region Fields

        [SerializeField()]
        [TypeReference.Config(typeof(Component), dropDownStyle=TypeDropDownListingStyle.ComponentMenu)]
        private TypeReference _componentType;

        [SerializeField()]
        [TriggerableTargetObject.Config(typeof(GameObject))]
        private TriggerableTargetObject _target;

        [SerializeField()]
        [UnityEngine.Serialization.FormerlySerializedAs("AddMultipleIfExists")]
        [Tooltip("Add a new component even if one already exists.")]
        private bool _addMultipleIfExists;

        #endregion

        #region Properties

        public bool AddMultipleIfExists
        {
            get { return _addMultipleIfExists; }
            set { _addMultipleIfExists = value; }
        }

        #endregion

        #region TriggerableMechanism Interface

        public override bool Trigger(object sender, object arg)
        {
            if (!this.CanTrigger) return false;

            var targ = _target.GetTarget<GameObject>(arg);
            if (targ == null) return false;

            try
            {
                if (!this._addMultipleIfExists && targ.HasComponent(_componentType.Type)) return false;

                var comp = targ.AddComponent(_componentType.Type);
                return true;
            }
            catch
            {
            }

            return false;
        }

        #endregion

    }
}

Though… again, this is a fairly complex structure I designed here for anyone who is novice to programming and C#… so I might advise you to stick to the enum setup I described.

1 Like

Thanks everyone! Problem solved :slight_smile: