Version: 6000.3
语言: 中文
跟踪垃圾回收分配
原生内存

避免 C# 反射开销

两个单声道Unity 中使用的脚本后端。更多信息
请参阅术语表
IL2CPP Unity 开发的脚本后端,在为某些平台构建项目时,可以将其用作 Mono 的替代方案。更多信息
请参阅术语表
脚本后端为Unity中的脚本提供支持的框架。Unity 支持三种不同的脚本后端,具体取决于目标平台:Mono、.NET 和 IL2CPP。但是,通用 Windows 平台仅支持两个:.NET 和 IL2CPP。更多信息
请参阅术语表
内部缓存所有 C#System.Reflection对象,Unity 不会对它们进行垃圾回收。这意味着垃圾回收器在应用程序的生存期内会持续扫描缓存的 C# 反射对象,这会导致不必要且可能巨大的垃圾回收器开销。

若要最大程度地减少垃圾回收器开销,请避免使用在运行时创建大量 C# 反射对象的方法,例如Assembly.GetTypesType.GetMethods.相反,在 Unity 编辑器中扫描程序集以查找所需的数据并将其序列化,或者使用代码生成使数据在运行时可用。

在编辑器中扫描装配

以下示例演示如何在编辑器中扫描程序集、序列化数据,并在运行时使用它而不进行反射。假设您要查找实现名为IMyFeature并在运行时使用该信息。您可以编写一个编辑器脚本来扫描实现的所有类型IMyFeature并将其名称保存到ScriptableObject或 JSON 文件,如下所示:

using UnityEngine;
using UnityEditor;
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;

[CreateAssetMenu(menuName = "Feature/FeatureTypeList")]
public class FeatureTypeList : ScriptableObject
{
    public List<string> typeNames;
}

public static class FeatureTypeScanner
{
    [MenuItem("Tools/Scan IMyFeature Types")]
    public static void ScanTypes()
    {
        var featureType = typeof(IMyFeature);
        var types = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(a => a.GetTypes())
            .Where(t => featureType.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
            .Select(t => t.FullName)
            .ToList();

        // Create/overwrite the asset
        var asset = ScriptableObject.CreateInstance<FeatureTypeList>();
        asset.typeNames = types;
        AssetDatabase.CreateAsset(asset, "Assets/FeatureTypeList.asset");
        AssetDatabase.SaveAssets();

        Debug.Log($"Found {types.Count} types implementing IMyFeature. Saved to FeatureTypeList.asset");
    }
}

从菜单中运行工具>扫描 IMyFeature 类型。这将创建Assets/FeatureTypeList.asset包含类型名称列表。

在运行时,你可以加载此资产,并通过以下方式使用类型名称进行实例化Activator.CreateInstance,或仅用于展示。如果使用 codegen,则不需要反射,如下所示:

public class FeatureTypeLoader : MonoBehaviour
{
    public FeatureTypeList featureTypes;

    void Start()
    {
        var assemblyName = this.GetType().Assembly.FullName;
        foreach (var typeName in featureTypes.typeNames)
        {
            Debug.Log("Instantiating feature type: " + typeName);
            Activator.CreateInstance(assemblyName, typeName);
            // You can also use Type.GetType(typeName) (both of these still uses reflection but only on the pre-selected feature types instead of casting a wider net across all sort of types in the assembly.)
        }
    }
}

使用代码生成

您可以在编辑器时生成一个包含类型或委托静态数组的 C# 文件,而不是存储类型名称,从而消除所有运行时反射。以下代码示例生成一个文件Assets/GeneratedFeatureTypes.cs使用已发现类型的静态数组,您可以在运行时使用该数组,而无需反射:

using UnityEditor;
using System;
using System.Linq;
using System.Text;
using System.IO;

public static class FeatureTypeCodeGen
{
    [MenuItem("Tools/CodeGen IMyFeature Types")]
    public static void CodeGenTypes()
    {
        var featureType = typeof(IMyFeature);
        var types = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(a => a.GetTypes())
            .Where(t => featureType.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
            .ToList();

        StringBuilder sb = new StringBuilder();
        sb.AppendLine("public static class GeneratedFeatureTypes {");
        sb.AppendLine("    public static readonly System.Type[] Types = new System.Type[] {");
        foreach (var t in types)
        {
            sb.AppendLine($"        typeof({t.FullName}),");
        }
        sb.AppendLine("    };");
        sb.AppendLine("}");

        File.WriteAllText("Assets/GeneratedFeatureTypes.cs", sb.ToString());
        AssetDatabase.Refresh();
        Debug.Log("Generated FeatureTypes.cs with " + types.Count + " types.");
    }
}

其他资源

跟踪垃圾回收分配
原生内存