簡體   English   中英

C# 如何通過反射獲得一個字段並創建一個返回其值以供將來使用的委托?

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

我的用例:在我的游戲中,我有一個覆蓋層,用於跟蹤具有“[TrackedField]”屬性的任何字段。 我希望它顯示變量的名稱和當前值。 反射是一個有點昂貴的操作,我一直在尋找一種方法來通過反射檢索一次值,然后創建一個不使用反射的委托函數來返回字段的值。 這樣,只要我想更新疊加層上的值,就可以調用它。

我實際上不知道我要問的是否可行,或者是否有更好的方法來檢索此值。 過去幾天我已經四處搜索,但我能挖掘到的只是這個相關的帖子 它很可能每秒更新多次,所以如果可以的話,我想避免重復使用反射。

目前,我的代碼只獲取每個變量名稱(或屬性中定義的標簽),並使用一個僅讀取“錯誤”的虛擬委托來顯示它:

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

以下是“[TrackedField]”屬性的示例:

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

如果你很好奇,它會在覆蓋層上產生這個結果。

如果您對屬性的外觀感興趣:

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

我從未使用過 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);
    }
}

用法:

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);

我也沒有使用過Unity。 您可以嘗試替換您的委托() => { return "error"; } () => { return "error"; }

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

請注意,此委托將返回一個對象,以防萬一您應該處理null值。

此外,它仍然是一個昂貴的調用,但至少你減少了查找字段和枚舉屬性,這些操作是高成本的操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM