简体   繁体   English

C# 如何通过反射获得一个字段并创建一个返回其值以供将来使用的委托?

[英]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.我的用例:在我的游戏中,我有一个覆盖层,用于跟踪具有“[TrackedField]”属性的任何字段。 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]”属性的示例:

[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; }
    }
}

I have never used Unity but in in classic .NET Framework this is possible:我从未使用过 Unity,但在经典的 .NET Framework 中这是可能的:

public class FieldAccessor
{
    private delegate object FieldGetter(object instance);       
    private FieldGetter getter;

    public FieldAccessor(FieldInfo field)
    {
        ParameterExpression instanceParameter = Expression.Parameter(typeof(object), "instance");

        MemberExpression member = Expression.Field(
            field.IsStatic ? null : Expression.Convert(instanceParameter, field.DeclaringType), // (TInstance)instance
            field);

        LambdaExpression lambda = Expression.Lambda<FieldGetter>(
            Expression.Convert(member, typeof(object)), // object return type
            instanceParameter); // instance (object)

        getter = (FieldGetter)lambda.Compile();
    }

    public object Get(object instance)
    {
        return getter(instance);
    }
}

Usage:用法:

FieldInfo fi = typeof(MyType).GetField(...);

// creating the accessor is slow so cache this accessor instance:
var accessor = new FieldAccessor(fi);

// and then just use it like this:
var value = accessor.Get(myInstance);

I haven't used Unity as well.我也没有使用过Unity。 You may try to replace your delegate () => { return "error"; }您可以尝试替换您的委托() => { return "error"; } () => { return "error"; } with () => { return "error"; }

() => { return objectFields[i].GetValue(mono); }

Note that this delegate will return an object and just in case you should handle null values.请注意,此委托将返回一个对象,以防万一您应该处理null值。

Also, it is still an expensive call but at least you cut on finding fields and enumerating attributes that are high expensive operations.此外,它仍然是一个昂贵的调用,但至少你减少了查找字段和枚举属性,这些操作是高成本的操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM