dimanche 22 octobre 2017

C# How would I get a field through reflection once and make a delegate that returns its value for future use?

My use case: In my game I have an overlay that tracks any field with the '[TrackedField]' attribute. I want it to display the name of the variable, and the current value. Reflection being a bit of an expensive operation, I was looking for a way to retrieve the value once through reflection, and then make a delegate function, which doesn't use reflection, that returns the field's value. That way it can be called whenever I want to update that value on the overlay.

I actually don't know if what I'm asking is even possible, or if there would be a better way to retrieve this value. I've searched around for the past couple days, but all I was able to dig up was this related post. It will most likely be updated multiple times a second, so I'd like to avoid repeated use of reflection if I can.

Currently my code just gets every variable name (or a label defined in the attribute) and displays it with a dummy delegate that just reads "error":

MonoBehaviour[] sceneActive = GameObject.FindObjectsOfType<MonoBehaviour>();

foreach (MonoBehaviour mono in sceneActive)
{
    System.Type monoType = mono.GetType();

    // Retreive the fields from the mono instance
    FieldInfo[] objectFields = monoType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

    // search all fields and find the attribute [TrackedField]
    for (int i = 0; i < objectFields.Length; i++)
    {
        TrackedFieldAttribute attribute = Attribute.GetCustomAttribute(objectFields[i], typeof(TrackedFieldAttribute)) as TrackedFieldAttribute;

        // if we detect any attribute add it to the overlay
        if (attribute != null)
        {
            trackerBar.AddTab(attribute.label == null ? objectFields[i].Name : attribute.label, () => { return "error"; },attribute.color);
        }
    }
}

Here's an example of the '[TrackedField]' attribute in action:

[TrackedField]
private bool consoleOpen = false;
[TrackedField("MyLabel")]
private bool overlayShown = false;
[TrackedField("ColoredLabel", 50, 50, 255)]

It results in this on the overlay, if you were curious.

And if you were interested in what the attribute looked like:

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class TrackedFieldAttribute : Attribute
{
    private string _label;
    private Color _color;

    public TrackedFieldAttribute()
    {
        _label = null;
        _color = default(Color);
    }

    public TrackedFieldAttribute(string label)
    {
        _label = label;
        _color = default(Color);
    }

    public TrackedFieldAttribute(float red = 0, float green = 0, float blue = 0)
    {
        _label = null;
        _color = new Color(red / 255f, green / 255f, blue / 255f);
    }

    public TrackedFieldAttribute(string label, float red = 0, float green = 0, float blue = 0)
    {
        _label = label;
        _color = new Color(red / 255f, green / 255f, blue / 255f);
    }

    public string label
    {
        get { return _label; }
        set { _label = value; }
    }

    public Color color
    {
        get { return _color; }
        set { _color = value; }
    }
}





Aucun commentaire:

Enregistrer un commentaire