Reflection problem in C#

Started by
7 comments, last by marvel_magnum 9 years, 6 months ago
I am writing a small game in Unity3D. There are special effects for the units in the game which take effect and are customizable.
I have written a custom script so that the effects can be created on the fly through a data fly.
Eg. The below script creates a buffID "Naptha" which give all archers in the player's army a fire arrow; fire damage is 15% of the base attack value.
[source]
#buff Naptha
{
mod(SELF,[ARCHER],SpecialType = FIRE)
mod(SELF,[ARCHER],SpecialDamage = AttackDamage * 0.15)
}
#end
[/source]
A parser parses the file into recognizable tokens.
All the stuff in CAPS are enum types. and SpecialDamage, AttackDamage are float properties of the Unit Class among others. Now the idea is to collect the values from the specified properties using reflection, calculate them and then feed back the result.
Now I can extract the value of a property like so --
[source]object value1 = testUnit.GetType().GetProperty("AttackDamage").GetGetMethod().Invoke(testUnit, null);[/source]
I can even get the type of the property like so --
[source]Type baseStatType = testUnit.GetType().GetProperty("AttackDamage").PropertyType[/source]
Here come my problem. I cannot perform calculations with object types and I am not able to cast it to a type first as even the type is determined through reflection. How to proceed with this ?
Basically I want something like this to work ---
[source]
Type baseStatType = testUnit.GetType().GetProperty("AttackDamage").PropertyType
object value2 = testUnit.GetType().GetProperty("AttackDamage").GetGetMethod().Invoke(testUnit, null)
baseStatType value1 = (baseStatType) value2 * (baseStatType) param;
PropertyInfo result = testUnit.GetType().GetProperty("SpecialDamage");
result.SetValue(testUnit, value1, null);
[/source]
The other way I can think of (but dont want to do) is a dirty switch case based on the propertyname. sad.png
Advertisement

What about "dynamic"?


object a = 10;
object b = 0.5f;
dynamic c = (dynamic)a * (dynamic)b;
object result = c;

This will "just work", even when a and b are more complex types with custom multiplication operators, because the CLR will do at runtime what the normal compiler would have done at compile-time. It will even optimize it for you during the first invocation. However, I have never used Unity and have no idea if that feature of C# is supported.

The alternatives would be to either re-implement C#'s entire type system or to restrict your scripts to a few well-known type and do a switch on these. Do you really intend to use any types besides float, int and enums in these scripts?

current project: Roa

Unity doesn't have Dynamic Language Runtime yet as far as I know. It's effectively still C# 3.0 / .Net 3.5. sad.png

Can you use Convert.ToSingle() ?

You can implement 'dynamic' using the old ways:


if (someObject is float)
{
    float value = (float)someObject;
    // do stuff with it.
}
else if (someObject is YourEnumType)
{
    YourEnumType value = (YourEnumType)someObject;
    // do stuff with it.
}

Or a Dictionary<Type,Action<object>>:

table[typeof(float)] = FunctionFloat;
table[typeof(YourEnumType)] = FunctionYourEnumType;

void FunctionFloat(object o)
{
    var value = (float)o;
    // do stuff
}

void FunctionYourEnumType(object o)
{
   var value = (YourEnumType)o;
   // do stuff
}

And call it with:

table[someObject.GetType()](someObject);

Obviously this gets out of hand quite quickly.

To expand on ferrous' reply, I would probably just convert all your values to a common type (Single, Double, etc.) for the purpose of performing calculations, then then convert the result back to the proper result type when finished.

You could use generic methods for arithmetic operators such as those provided by MiscUtil (http://www.yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html), invoking, for example, Multiply<T>(T, T) through reflection.

However, that seems a bit overkill. Is there any reason you can't just cast the stat value from object to float when you're performing the arithmetic?


Is there any reason you can't just cast the stat value from object to float when you're performing the arithmetic?

I cannot because I would not know the actual type of the stat that has been read in.


Dictionary<Type,Action<object>>:

table[typeof(float)] = FunctionFloat;
table[typeof(YourEnumType)] = FunctionYourEnumType;

void FunctionFloat(object o)
{
var value = (float)o;
// do stuff
}

void FunctionYourEnumType(object o)
{
var value = (YourEnumType)o;
// do stuff
}

And call it with:

table[someObject.GetType()](someObject);

This should work for me. I have int, float and 3 types of enums among all my stats so it should be manageable.

Even Jeff's suggestion should work for me.


Multiply<T>(T, T) through reflection.

I need to get down to the board and find out which one seems better. Will post solution soon. Thanks a lot guys. smile.png

This is how I did it in the end. Comments and suggestions are welcome as usual. :)

[source]
_operationMap = new Dictionary<Type, Delegate.dlgOperation>();
_operationMap.Add(typeof(float), OperationFloat);
...
// Snippet from method where effects are getting applied....
...
foreach (Unit unit in targetUnits)
{
object op1 = unit.GetType().GetProperty(modifier.effect.baseStat).GetGetMethod().Invoke(unit, null);
object op2 = modifier.effect.value;
PropertyInfo result = unit.GetType().GetProperty(modifier.effect.resultStat);
object retValue = _operationMap[op1.GetType()](op1, op2, modifier.effect.operation);
result.SetValue(unit, retValue, null);
}
...
// One of the handler functions
private object OperationFloat(object op1, object op2, BuffsDatabase.Operation operation)
{
float o1 = (float)op1;
float o2 = float.Parse(op2 as String);
float result = 0f;
switch (operation)
{
case BuffsDatabase.Operation.ASSIGN: result = o2; break;
case BuffsDatabase.Operation.ADD: result = o1 + o2; break;
case BuffsDatabase.Operation.MULTIPLY: result = o1 * o2; break;
default: DebugUtils.Error("ERROR: Invalid Operation encountered in OperationFloat()!"); break;
}
return result as object;
}
[/source]
Thing works like a charm now.. Thanks for all your help guys. Love ya!

This topic is closed to new replies.

Advertisement