0
\$\begingroup\$

I am trying to get access to a set of stored variables inside a scriptible object in Unity. This class (SectorDeclaration) has a method to pull a variable with a string of the variables name from it:

public Weightings[] GetVariName(string variableName)
{
    Type this_class = Type.GetType("SectorDeclaration");
    System.Reflection.FieldInfo fieldtype = this_class.GetField(variableName);
    Weightings[] fieldFind = (Weightings[])fieldtype.GetValue(null);
    return (Weightings[])fieldFind;
}

Here's the error I'm getting:

TargetException: Non-static field requires a target System.Reflection.MonoField.GetValue (System.Object obj) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoField.cs:108) SectorDeclaration.GetVariName (System.String variableName) (at Assets/Code/Gameplay Element/level Elements/SectorDeclaration.cs:54) BaseGameManager.InitializeNPCSystem () (at Assets/Code/Managers/BaseGameManager.cs:87) BaseGameManager.Start () (at Assets/Code/Managers/BaseGameManager.cs:73)

It works until the code reaches Weighting[] fieldFind = (Weighting[])Fieldtype.GetValue(null); I've used this code in another area but it is not working here, I've tried with System.object obj but it comes up with namespace doesn't contain this type, as well as with the type I am trying to access. Honestly, not sure what's wrong. If you need more information please let me know.

\$\endgroup\$
  • \$\begingroup\$ What's the difference between the code that is working somewhere else and the code that is not working? That might be a hint as to why it's not working here. \$\endgroup\$ – slaphshot33324 Nov 20 '18 at 19:21
  • \$\begingroup\$ that code is in a static class, so I know the null works there. \$\endgroup\$ – TartanKavuJr Nov 20 '18 at 19:56
  • \$\begingroup\$ Please keep in mind that reflection is a language feature which is best avoided. There are very few edge-cases where you must use reflection. Most of the time it can be avoided by using the right data structures and object-oriented patterns. In this case you seem to want to access some key/value data store. When that is the case, store your values in a Dictionary. \$\endgroup\$ – Philipp Nov 23 '18 at 9:41
1
\$\begingroup\$

The error identifies exactly what the problem is:

TargetException: Non-static field requires a target
System.Reflection.MonoField.GetValue (System.Object obj)

Since the field isn't static, that means the values of the field are stored on an instance of the type. So when you ask the FieldInfo to fetch the values for you, you need to tell it from which instance you want it to fetch them. (You might have a dozen SectorDeclaration instances in your game, and it doesn't know one from the other).

The way to give it a target from which to fetch values is the obj argument. As shown in the docs, you set that to null only when the field you want to read is static. Otherwise, it should be set to the instance you want to read from.

Assuming this method is inside the SectorDeclaration type, and you want the values from this instance, you can pass this as the argument:

Weightings[] fieldFind = (Weightings[])fieldtype.GetValue(this);

Of course, in the case where we already know what type we're fetching these values from, and it's one under our control, we could bypass the use of reflection entirely in a couple of different ways.

For instance, we could use a switch statement, using an enum to identify the different fields we might want to use (which avoids "stringly-typed" code, though you can use Enum.Parse if your source absolutely must use strings):

public class SectorDeclaration : ScriptableObject {

    // I have no idea what these weights represent, so I'm spitballing here...
    public enum WeightingCategory {
        LoadPriorityWeights,
        SpawnProbabilityWeights,
        BiomeDistributionWeights
    }

    public Weightings[] loadPriorities;
    public Weightings[] spawnProbabilities;
    public Weightings[] biomeDistribution;

    public Weightings[] GetWeightingsByCategory(WeightingCategory category) {
        switch(category) {
            case WeightingCategory.LoadPriorityWeights: return loadPriorities;
            case WeightingCategory.SpawnProbabilityWeights: return spawnProbabilities;
            case WeightingCategory.BiomeDistributionWeights: return biomeDistribution;
            default: return null;
        }
    }

    public Weightings[] GetWeightingsByCategory(string categoryName) {
        return GetWeightingsByCategory((WeightingCategory)System.Enum.Parse(WeightingCategory, categoryName));
    }
}

This will likely be substantially faster at runtime and less bug-prone than using reflection.

\$\endgroup\$
  • \$\begingroup\$ Amazing, I was reading through the documentation and couldn't fully comprehend it, so thanks for clearing that up, stupid really considering I've built a state machine. as for the suggestion this class is going to be polymorphic so its going to be easier to use this string identifier down the line thanks for the fix, and I'll reply with my results. Your other suggestion is much appreciated. Thank-you so much, such a stupid misunderstanding. \$\endgroup\$ – TartanKavuJr Nov 21 '18 at 1:04
  • \$\begingroup\$ I'd be willing to bet we can come up with a non-reflection solution to your polymorphism case too, if we learn a bit more about what problem this class hierarchy is solving. ;) \$\endgroup\$ – DMGregory Nov 21 '18 at 1:06
  • \$\begingroup\$ thanks but I can't disclose that :/. luckily this runs only once 9 times at level start, so runspeed doesnt matter as much, thanks for the help though. Plus I'm pulling different classes instead of just weightings and i know how to sort that part lol. \$\endgroup\$ – TartanKavuJr Nov 21 '18 at 1:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.