借助Atrribute扩展UnityEdior
C# Attribute 简介
Attribute 是 C# 提供的一种强大的元数据机制,可以用来为代码的程序元素(如类、方法、属性等)附加额外的信息。这些附加信息可以在运行时通过反射机制读取,从而影响程序的行为。
Attribute 的特性
-
轻量级
Attribute 不会直接影响代码运行,只是附加额外信息。
-
可扩展
开发者可以创建自定义 Attribute。
-
强大
常用于代码标记、配置、工具生成、验证等场景。
Attribute 的应用范围
可以应用到以下程序元素:
- 类、结构、枚举
- 方法、构造函数
- 属性、字段
- 接口、委托
- 参数、返回值
预定义的 Attribute 示例
C# 提供了许多内置 Attribute,例如:
-
[Obsolete]
- 标记某个成员为已过时。
[Obsolete("This method is deprecated. Use NewMethod instead.")] public void OldMethod() { }
-
[Serializable]
- 标记类可以序列化。
[Serializable] public class MyClass { }
-
[DllImport]
- 用于调用非托管代码。
[DllImport("user32.dll")] public static extern int MessageBox(int hWnd, string text, string caption, uint type);
-
[Test]
- 单元测试框架中常用的 Attribute(例如 NUnit 或 MSTest)。
[Test] public void MyTestMethod() { }
自定义 Attribute
创建自定义 Attribute
- 定义一个继承自
System.Attribute
的类。 - 使用
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
{// 定义逻辑
}
常见场景
- 标记元数据
- 使用 Attribute 附加描述信息,例如文档注释、开发工具的自动生成代码标记。
- 代码验证
- 结合反射读取 Attribute,用于校验输入数据。
- 控制序列化
- 通过 Attribute 标记需要序列化的字段或属性,例如 JSON 序列化。
- 单元测试
- 框架(如 NUnit、MSTest)通过 Attribute 标记测试方法。
- 绑定 UI 或框架
- 框架(如 Unity、ASP.NET)通过 Attribute 简化绑定,例如
UnityEngine.SerializeField
。
- 框架(如 Unity、ASP.NET)通过 Attribute 简化绑定,例如
优点与注意事项
优点:
- 清晰地描述额外信息。
- 可扩展性强,便于元编程。
- 通过反射可以动态控制代码行为。
注意事项:
-
性能
Attribute 的过多使用可能影响性能,尤其是需要频繁反射时。
-
复杂性
需要熟悉反射机制才能正确使用 Attribute。
-
局限性
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 fieldRect 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" buttonRect buttonPosition = new Rect(position.x + position.width - 75, position.y, 75, position.height);if (GUI.Button(buttonPosition, "Browse")){// Open file panelstring 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;
}
总结
C# 的 Attribute 提供了一种灵活的元数据管理机制,广泛应用于框架、工具和自定义开发场景中。无论是使用预定义 Attribute,还是创建自己的 Attribute,都能极大提高代码的可读性和可扩展性。