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:
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);
}
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:
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.