Find(bind) component or Gameobject by c# attribute

I seek for someone to review my code and give some suggestion, thanks.

/*    Code below is use for handle attached component on children or GameObject  by C# Attribute. my email : smartether@gmail.com  **/
/*  一下代码用于通过c#的注解来获取组件或者gameobject   **/
using UnityEngine;
using System.Collections;

[System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Field)]
public class PropertyAtt : System.Attribute
{
    public static void Init(MonoBehaviour mb, string path = "")
    {
        System.Type tp = mb.GetType();
        System.Reflection.FieldInfo[] fieldInfos = tp.GetFields(System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        foreach (System.Reflection.FieldInfo fieldInfo in fieldInfos)
        {
            System.Attribute[] atts = fieldInfo.GetCustomAttributes(typeof(PropertyAtt), false) as System.Attribute[];
            if (atts != null && atts.Length > 0)
            {
                PropertyAtt att = atts[0] as PropertyAtt;
                object value = null;
                if(att.m_isComponent){
                    value = mb.transform.FindChild(string.Concat(path, att.m_findWithFieldName ? fieldInfo.Name.Replace("m_", "") : att.m_TargetName)).GetComponent(fieldInfo.FieldType);
                }
                else{
                    value = mb.transform.FindChild(string.Concat(path, att.m_findWithFieldName ? fieldInfo.Name.Replace("m_", "") : att.m_TargetName)).gameObject;
                }
                fieldInfo.SetValue(mb, value);
            }
        }
    }
    public string m_TargetName { get; set; }
    public bool m_isComponent { get; set; }
    /* fieldName = m_Name  => targetName = Name **/
    public bool m_findWithFieldName { get; set; }

    public PropertyAtt(bool isComponent = true, string TargetName= "")
    {
        m_isComponent = isComponent;
        m_findWithFieldName = TargetName == string.Empty;
        if(!m_findWithFieldName)
            m_TargetName = TargetName;
    }

    public PropertyAtt(string TargetName , bool isComponent = true)
    {
        m_isComponent = isComponent;
        m_findWithFieldName = TargetName == string.Empty;
        if (!m_findWithFieldName)
            m_TargetName = TargetName;
    }
}
/********Use**example************/
    [PropertyAtt("BagFram")]
    UITexture m_BagFram;
    [PropertyAtt("BagImage")]
    UITexture m_BagImage;
    [PropertyAtt("BagTypeIcon")]
    UISprite m_BagTypeIcon;
    [PropertyAtt("BagTypeName")]
    UISprite m_BagTypeName;
    [PropertyAtt("BagIntroduce")]
    UISprite m_BagIntroduce;
    [PropertyAtt("SpecialLabelGrid")]
    UIGrid m_SpecialLabelGrid;
    [PropertyAtt("DrawOneBtn")]
    UISprite m_DrawOneBtn;
    [PropertyAtt("DrawOneBtn/NeedNum")]
    UILabel m_DrawOneNeedNum;
    [PropertyAtt("DrawOneBtn/MoneyIcon")]
    UISprite m_DrawOneMoneyIcon;
    [PropertyAtt("DrawTenBtn")]
    UISprite m_DrawTenBtn;
    [PropertyAtt("DrawTenBtn/NeedNum")]
    UILabel m_DrawTenNeedNum;
    [PropertyAtt("DrawTenBtn/MoneyIcon")]
    UISprite m_DrawTenMoneyIcon;
    [PropertyAtt("FreeColdDown")]
    UILabel m_FreeColdDown;
    [PropertyAtt("FreeNum")]
    UILabel m_FreeNum;
    [PropertyAtt("FreeMax")]
    UILabel m_FreeMax;
    void Awake()
    {
        PropertyAtt.Init(this, "MainWidget/");
    
    }

how about you use some CODE tags to make it easier for us to read.

I have add Code tags.:slight_smile:

##############

  1. .Net naming standards suggests naming your attributes along the lines of : NameOfAttribAttribute. So in your case something like:
public class PropertyAttribute : System.Attribute

Though PropertyAttribute is already the name of an attribute in UnityEngine namespace, so I’d probably be more descriptive with it. How about name it for what this attribute IS. This appears to map a field to an object of the child of the GameObject the script is attached to. So maybe…

public class RetrieveMemberByPathAttribute : System.Attribute

or something like that.

What’s nice about this is that the C# compiler can imply the ‘Attribute’ part of the name. So when you set the attribute onto some field or class you can omit the Attribute and say:

[RetrieveMemberByPath("FreeNum")]
UILabel m_FreeNum;

##############
2) some little things
like your string equality testers… what if I pass “null” as my path? Is that mean the same as string.Empty? Maybe test with ‘string.IsNullOrEmpty’ rather than comparing to ‘string.Empty’.

the line:

System.Attribute[] atts = fieldInfo.GetCustomAttributes(typeof(PropertyAtt), false) as System.Attribute[];

This will actually return an array typed to the type passed in to the method. You could say:

PropertyAtt[] atts = fieldInfo.GetCustomAttributes(typeof(PropertyAtt), false) as PropertyAtt[];

##############
3) What is ‘m_isComponent’ for? Couldn’t this be inferred from the type of the field. In your code, if m_isComponent is false, you just set the field to the GameObject. But to do that, the field would HAVE to be a GameObject.

if(typeof(GameObject).IsAssignableFrom(fieldInfo.FieldType))
    //get gameobject
else
    //get component

##############
4) Why are you doing this at Runtime? This reflective code having to run every time that the script is created is going to be slow.

Why not do this at editor time?

Instead of this “Init” method called in Awake. You instead make your attribute a UnityEngine.PropertyAttribute, and then make a custom PropertyDrawer that pulled the value value for you if the value is null.

I have several PropertyDrawers similar to this in my framework, and that’s how I do it.

Thanks for your help, I will read serioulsy, I do this for managing code in svn.