借助Atrribute扩展UnityEdior

借助Atrribute扩展UnityEdior

image-20241231141303804

C# Attribute 简介

Attribute 是 C# 提供的一种强大的元数据机制,可以用来为代码的程序元素(如类、方法、属性等)附加额外的信息。这些附加信息可以在运行时通过反射机制读取,从而影响程序的行为。


Attribute 的特性

  1. 轻量级

    Attribute 不会直接影响代码运行,只是附加额外信息。

  2. 可扩展

    开发者可以创建自定义 Attribute。

  3. 强大

    常用于代码标记、配置、工具生成、验证等场景。


Attribute 的应用范围

可以应用到以下程序元素:

  • 类、结构、枚举
  • 方法、构造函数
  • 属性、字段
  • 接口、委托
  • 参数、返回值

预定义的 Attribute 示例

C# 提供了许多内置 Attribute,例如:

  1. [Obsolete]

    • 标记某个成员为已过时。
    [Obsolete("This method is deprecated. Use NewMethod instead.")]
    public void OldMethod() { }
    
  2. [Serializable]

    • 标记类可以序列化。
    [Serializable]
    public class MyClass { }
    
  3. [DllImport]

    • 用于调用非托管代码。
    [DllImport("user32.dll")]
    public static extern int MessageBox(int hWnd, string text, string caption, uint type);
    
  4. [Test]

    • 单元测试框架中常用的 Attribute(例如 NUnit 或 MSTest)。
    [Test]
    public void MyTestMethod() { }
    

自定义 Attribute

创建自定义 Attribute

  1. 定义一个继承自 System.Attribute 的类。
  2. 使用 AttributeUsage 控制 Attribute 的适用范围和行为。
[System.AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class MyCustomAttribute : Attribute
{
    public string Description { get; }

    public MyCustomAttribute(string description)
    {
        Description = description;
    }
}

使用自定义 Attribute

[MyCustomAttribute("This is a sample class.")]
public class SampleClass
{
    [MyCustomAttribute("This is a sample method.")]
    public void SampleMethod() { }
}

读取自定义 Attribute

通过反射读取附加的 Attribute 信息:

var type = typeof(SampleClass);
var attributes = type.GetCustomAttributes(typeof(MyCustomAttribute), false);

foreach (MyCustomAttribute attr in attributes)
{
    Console.WriteLine($"Description: {attr.Description}");
}

AttributeUsage 属性

AttributeUsage 是用来定义自定义 Attribute 的使用规则的 Attribute。

[AttributeUsage(
    AttributeTargets.Class | AttributeTargets.Method, // 应用范围
    AllowMultiple = true,                             // 是否允许多次使用
    Inherited = true)]                                // 是否能被继承
public class MyCustomAttribute : Attribute
{
    // 定义逻辑
}

常见场景

  1. 标记元数据
    • 使用 Attribute 附加描述信息,例如文档注释、开发工具的自动生成代码标记。
  2. 代码验证
    • 结合反射读取 Attribute,用于校验输入数据。
  3. 控制序列化
    • 通过 Attribute 标记需要序列化的字段或属性,例如 JSON 序列化。
  4. 单元测试
    • 框架(如 NUnit、MSTest)通过 Attribute 标记测试方法。
  5. 绑定 UI 或框架
    • 框架(如 Unity、ASP.NET)通过 Attribute 简化绑定,例如 UnityEngine.SerializeField

优点与注意事项

优点:

  • 清晰地描述额外信息。
  • 可扩展性强,便于元编程。
  • 通过反射可以动态控制代码行为。

注意事项:

  1. 性能

    Attribute 的过多使用可能影响性能,尤其是需要频繁反射时。

  2. 复杂性

    需要熟悉反射机制才能正确使用 Attribute。

  3. 局限性

    Attribute 不支持运行时动态修改。


示例:Unity编辑器窗口获取文件路径

#if UNITY_EDITOR
using UnityEditor;
#endif
using System;
using UnityEngine;

/// <summary>
/// Custom attribute to allow path selection in the Unity editor.
/// </summary>
public class PathSelectAttribute : PropertyAttribute
{
    public string Title { get; private set; }
    public string Extension { get; private set; }

    public PathSelectAttribute(string title = "Select File", string extension = "*")
    {
        Title = title;
        Extension = extension;
    }
}

#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(PathSelectAttribute))]
public class PathSelectDrawer : PropertyDrawer
{
    private string _lastSelectedPath = "";
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        if (property.propertyType != SerializedPropertyType.String)
        {
            EditorGUI.LabelField(position, label.text, "Use PathSelect with string.");
            return;
        }

        PathSelectAttribute pathAttribute = (PathSelectAttribute)attribute;

        EditorGUI.BeginProperty(position, label, property);

        // Display the current string field
        Rect textFieldPosition = new Rect(position.x, position.y, position.width - 80, position.height);
        if(!string.IsNullOrEmpty(_lastSelectedPath))
            property.stringValue= _lastSelectedPath;
        
        property.stringValue = EditorGUI.TextField(textFieldPosition, label, property.stringValue);

        // Create the "Browse" button
        Rect buttonPosition = new Rect(position.x + position.width - 75, position.y, 75, position.height);
        if (GUI.Button(buttonPosition, "Browse"))
        {
            // Open file panel
            string path = EditorUtility.OpenFilePanel(pathAttribute.Title, "", pathAttribute.Extension);
            if (!string.IsNullOrEmpty(path))
            {
                //使用变量保存选择的路径,避免在GUI循环中修改属性值
                _lastSelectedPath = path;
            }
            else
            {
                _lastSelectedPath= String.Empty;
            }
            //类似打开窗口类型任务终止GUI循环时调用
            GUIUtility.ExitGUI();
        }

        EditorGUI.EndProperty();
    }
}

#endif
    
//------测试-------
/// <summary>
/// Example usage of PathSelectAttribute.
/// </summary>
public class PathSelectExample : MonoBehaviour
{
    [PathSelect("Select a JSON file", "json")]
    public string filePath;
}

image-20241231141318895

总结

C# 的 Attribute 提供了一种灵活的元数据管理机制,广泛应用于框架、工具和自定义开发场景中。无论是使用预定义 Attribute,还是创建自己的 Attribute,都能极大提高代码的可读性和可扩展性。

posted @ 2025-01-03 13:44  世纪末の魔术师  阅读(2)  评论(0编辑  收藏  举报