Zenject0.依据一哥们的总结反向总结1.不是吐槽这哥们是结合这大哥从不同面向学习Zenject2.本来无一物何处惹尘埃来自哥们的总结看得出总结很用心也是我们依据的关键比官方文档靠谱比一些教学视频靠谱1、原生的Instantiate游戏对象不会注入其定义的[Inject]属性需要使用Container.InstantiatePrefab来实例化才能受容器管控。这么写 Kernel 会空了必须通过Factory 创建不确定是不是 Zenject 6.x 最新版本的原因)public void Initialize() { //参考代码 //_prefabsFactory.CreatePlayerMonoEntity(Address.Prefabs.Player); prefabsFactory.Create(DataM2LevelSample); }2、类似的在SceneManager.LoadScene时如果Scene没有放置Zenject的SceneContext也无法呗容器管控其下的对象所定义的[Inject]属性也无法注入。SceneContext 倒是真的和ProjectContext 一样甚至更加”写死“难道缺了ProjectContentZenject这个框架能跑起来3、继承MonoBehavior的单例和普通Csharp单例可以分别如下定义Container.BindGameManager().FromNewComponentOnNewGameObject().AsSingle(); //自动放置新对象中Container.BindFruitManager().AsSingle();4、一些比较贴近Spring的使用4.1、正常的[Inject]、IInitializable、 IDisposable接口4.1.1、MonoBehavior脚本的全局单例Container.BindGameManager().FromNewComponentOnNewGameObject().AsSingle(); //自动放置新对象中4.2、ScriptObject数据作为全局配置参数Container.BindBackGroundColorConfigSO().FromInstance(backGroundColorConfigSO);Container.BindGameManager().FromNewComponentOnNewGameObject().AsSingle().WithArguments(backGroundColorConfigSO).NonLazy();4.3、Prefab预制体作为全局配置参数Container.BindGameObject().WithId(PlayerPrefab).FromInstance(playerPrefab);Container.BindPlayerAttribute().FromInstance(playerAttribute);Container.BindRespawnPlayerManager().FromNewComponentOnNewGameObject().AsSingle().WithArguments(playerPrefab, playerAttribute).NonLazy();如果你不知道怎么获取 预制体Prefab我的一个项目是用上了 addressable 获取Prefab)//注册 // Config Asset(原来了 GameplayInstaller.cs) diContainer.BindPlayerConfig() .FromScriptableObject(service.LoadAssetPlayerConfig(Address.Configurations.PlayerConfig)) .AsSingle(); diContainer.BindProjectileConfig() .FromScriptableObject(service.LoadAssetProjectileConfig(Address.Configurations.ProjectileConfig)) .AsSingle(); // UIModule Config diContainer.BindRocketConfig() .FromScriptableObject(service.LoadAssetRocketConfig(Address.Configurations.RocketConfig)) .AsSingle();5.补充前面问题1~2Scene 和GameObjectContent 如何关联待补充6.内部 Zenject 结构Zenject DiContainer 打印内部结构3 种方案可以打印容器全部绑定、内部字典、实例缓存、父子容器层级优先用内置 API精细化用反射读取私有字段。*(对比重点如上面说的:正常的[Inject]、IInitializable、 IDisposable接口 (小哥说的其他都不是重点(也可以看到 Zenject 内部 Context 和 SceneContext是区隔的如下对比图using System; using System.Collections.Generic; using System.Reflection; using System.Text; using UnityEngine; using Zenject; using static Zenject.DiContainer; public static class ZenjectDebugger { public static void PrintAllBindings(DiContainer container) { if (container null) { Debug.LogError(ZenjectDebugger: container 为空); return; } // 获取 _providers 字段 (注意是 _providers不是 _providerMap) FieldInfo providersField typeof(DiContainer).GetField(_providers, BindingFlags.NonPublic | BindingFlags.Instance); if (providersField null) { Debug.LogError(ZenjectDebugger: 未找到 _providers 字段可能是 Zenject 版本不兼容); return; } var providers providersField.GetValue(container) as DictionaryBindingId, ListProviderInfo; if (providers null) { Debug.LogWarning(ZenjectDebugger: _providers 为空或类型不匹配); return; } var sb new StringBuilder(); sb.AppendLine($ Zenject 容器绑定信息 (共 {providers.Count} 条) ); int index 1; foreach (var kvp in providers) { BindingId bindingId kvp.Key; ListProviderInfo providerInfos kvp.Value; // BindingId 包含类型和可选标识符 string typeName bindingId.Type?.Name ?? null; string identifier bindingId.Identifier?.ToString() ?? 无; sb.AppendLine($[{index}] 类型: {typeName}); if (bindingId.Identifier ! null) { sb.AppendLine($ 标识符: {identifier}); } // 打印每个 Provider 的信息 if (providerInfos ! null) { for (int i 0; i providerInfos.Count; i) { ProviderInfo providerInfo providerInfos[i]; sb.AppendLine($ Provider[{i}]: {GetProviderTypeInfo(providerInfo)}); } } sb.AppendLine(); index; } Debug.Log(sb.ToString()); } private static string GetProviderTypeInfo(ProviderInfo providerInfo) { if (providerInfo null) return null; // 尝试获取实际 Provider 实例 FieldInfo providerInstanceField typeof(ProviderInfo).GetField(Provider, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (providerInstanceField ! null) { object provider providerInstanceField.GetValue(providerInfo); if (provider ! null) { return provider.GetType().Name; } } return Unknown; } // 可选按类型筛选打印 public static void PrintBindingsForTypeT(DiContainer container) { PrintBindingsForType(container, typeof(T)); } public static void PrintBindingsForType(DiContainer container, Type targetType) { if (container null || targetType null) return; FieldInfo providersField typeof(DiContainer).GetField(_providers, BindingFlags.NonPublic | BindingFlags.Instance); if (providersField null) return; var providers providersField.GetValue(container) as DictionaryBindingId, ListProviderInfo; if (providers null) return; var sb new StringBuilder(); sb.AppendLine($ 查找类型 [{targetType.Name}] 的绑定信息 ); bool found false; foreach (var kvp in providers) { if (kvp.Key.Type targetType || targetType.IsAssignableFrom(kvp.Key.Type)) { found true; string identifier kvp.Key.Identifier?.ToString() ?? 无; sb.AppendLine($类型: {kvp.Key.Type?.Name}, 标识符: {identifier}); } } if (!found) { sb.AppendLine(未找到该类型的绑定); } Debug.Log(sb.ToString()); } }//参考调用代码 ZenjectDebugger.PrintAllBindings(Container); ZenjectDebugger.PrintAllBindings(ContainerBridge._Container);6.1 方法一容器层次结构是天然的区分线索Zenject的DiContainer存在父子层次结构通过ParentContainers属性可以构建容器树。这是框架原生的区分逻辑子容器会继承父容器的绑定但可以拥有自己独立的实例。你可以通过反射获取DiContainer的_parentContainers字段类型为ListDiContainer来打印容器的父子关系 方法三利用上下文与绑定信息倒推容器如果只是为了调试输出可以不直接获取容器ID而是观察它内部绑定的特征来推断容器身份检查核心绑定ProjectContext中通常会绑定全局服务如IAnalyticsService而SceneContext中则绑定场景特有服务如ISceneManager。通过反射查看容器内特定的绑定类型可以判断容器的类型和用途。检查 InstallersDiContainer内部有一个_installers字段存放了已执行安装的安装器实例列表。通过反射读取这个列表可以知道是哪些Installer向该容器进行了注册从而推断容器场景归属。错误1提供了方法一和三你是不是觉得很贴心呢同问题一提供了Instantiate和 PrefabFactory 两个方法不行行方法一方法三Container.Instantiate_prefabFactory错误2一、核心概念先理清Installer负责向DiContainer注册依赖的脚本MonoInstaller/ScriptableObjectInstallerContextZenject 的容器载体ProjectContext/SceneContext/GameObjectContextInstaller 存储位置所有 Installer 最终都绑定在对应的 Context 上而非直接存在 DiContainer 中说人话没办法从 diContainer 获取 installer 但是Context 可以获取 installervar allInstaller ContainerBridge._Container.ResolveAllInstaller(); int count0; foreach(var installer in allInstaller) { Debug.Log($容器已安装{count} Installer: {installer.GetType().Name}); count; } Debug.Log(Zenject 容器数量 allInstaller.Count);你是不是觉得又很贴心又行了呢结果获取数量为0根本没用总结Zenject 适合什么项目话不多说了只说一句要是你的项目是一个新的项目那么Zenject NewInput Addressable UniTask所有新的框架Zenject 框架都有提供相关代码和案例一些案例是有的甚至是丰富有余并且有些代码值得参考和学习和借鉴‍♀️参考Unity Zenject的一些小Tips学习备忘这哥们还有几个github的开源项目呢