VContainer-comparing/comparing-to-zenject | 对比 Zenject

import {Inline} from '../../src/components/CodeBlockInTable'
import {CodeSizeGraph} from "../../src/components/CodeSizeGraph"

VContainer 相比 Zenject 的优势:

  • 性能更优。
  • 反射操作集中在容器构建阶段。
  • 代码更简洁。
  • VContainer 的功能经过精心挑选,不会在容器中注册面向数据的对象,也不会主动注入 View 组件。防止 DI 声明变得过于复杂。
    • Zenject 通常用于注入动态或以数据为中心的对象,但这很复杂。
    • 在 VContainer 中,推荐使用 MonoBehaviour 注入而不是注入到 MonoBehaviour。
    • Zenject 在场景启动时通过反射查找所有 GameObject,此操作会造成高昂开销;VContainer 则不会如此。

Code size (v1.3.0)

API 对照表

基础注册

Zenject VContainer
Container.Bind<Service>() .AsTransient() builder.Register<Service>(Lifetime.Transient)
Container.Bind<Service>() .AsCached() builder.Register<Service>(Lifetime.Scoped)
Container.Bind<Service>() .AsSingle() builder.Register<Service>(Lifetime.Singleton)
Container.Bind<IService>() .To<Service> .AsCached() builder.Register<IService,Service>(Lifetime.Scoped)
Container.Bind( typeof(IInitializable), typeof(IDisposable)) .To<Service>() .AsCached() builder.Register<Service>(Lifetime.Scoped) .As<IInitializable,IDisposable>()
Container.BindInterfacesTo<Service>() .AsCached() builder.Register<Service>(Lifetime.Scoped) .AsImplementedInterfaces()
Container.BindInterfacesAndSelfTo<Foo>() .AsCached() builder.Register<Service>(Lifetime.Scoped) .AsImplementedInterfaces() .AsSelf()
Container.BindInstance(obj) builder.RegisterInstance(obj)
Container.Bind<IService>() .FromInstance(obj) builder.RegisterInstance<IService>(obj)
Container.Bind( typeof(IService1), typeof(IService2)) .FromInstance(obj) builder.RegisterInstance(obj) .As<IService1,IService2>()
Container.Bind( typeof(IService1), typeof(IService2)) .FromInstance(obj) builder.RegisterInstance(obj) .As<IService1,IService2>()
Container.BindInterfacesTo<Service>() .FromInstance(obj) builder.RegisterInstance(obj) .AsImplementedInterfaces()
Container.BindInterfacesAndSelfTo<Service>() .FromInstance(obj) builder.RegisterInstance(obj) .AsImplementedInterfaces() .AsSelf()

组件注册

Zenject VContainer
Container.Bind<Foo>() .FromComponentInHierarchy() .AsCached(); builder.RegisterComponentInHierarchy<Foo>()
Container.Bind<Foo>() .FromComponentInNewPrefab(prefab) .AsCached() builder.RegisterComponentInNewPrefab(prefab, Lifetime.Scoped)
Container.Bind<Foo>() .FromNewComponentOnNewGameObject() .AsCached() .WithGameObjectName("Foo1") builder.RegisterComponentOnNewGameObject<Foo>( Lifetime.Scoped, "Foo1")
.UnderTransform(parentTransform) .UnderTransform(parentTransform)
.UnderTransform(() => parentTransform) .UnderTransform(() => parentTransform)

Factory 工厂模式对比

使用参数 parameter

public class Enemy
{
    readonly float speed;

    public Enemy(float speed)
    {
        this.speed = speed;
    }

    public class Factory : PlaceholderFactory<float, Enemy>;
    {
    }
}

Container.BindFactory<float, Enemy, Enemy.Factory>();
public class Enemy
{
    readonly float speed;

    public Enemy(float speed)
    {
        this.speed = speed;
    }
}

builder.RegisterFactory<float, Enemy>(speed => new Enemy(speed));

运行时使用参数并解析依赖关系

public class Enemy
{
    readonly Player player;
    readonly float speed;

    public Enemy(float speed, Player player)
    {
        this.player = player;
        this.speed = speed;
    }

    public class Factory : PlaceholderFactory<float, Enemy>;
    {
    }
}

Container.BindFactory<float, Enemy, Enemy.Factory>();
public class Enemy
{
    readonly Player player;
    readonly float speed;

    public Enemy(float speed, Player player)
    {
        this.player = player;
        this.speed = speed;
    }
}

builder.RegisterFactory<float, Enemy>(container =>
{
    var player = container.Resolve<Player>();
    return speed => new Enemy(speed, player);
}, Lifetime.Scoped);

使用 prefab 预制体

public class Enemy : MonoBehaviour
{
    Player player;

    [Inject]
    public void Construct(Player player)
    {
        this.player = player;
    }

    public class Factory : PlaceholderFactory<Enemy>
    {
    }
}

Container.BindFactory<Enemy, Enemy.Factory>()
    .FromComponentInNewPrefab(enemyPrefab);
public class Enemy : MonoBehaviour
{
    Player player;

    public void Construct(Player player)
    {
        this.player = player;
    }
}

builder.RegisterFactory<Enemy>(container =>
{
    var player = container.Resolve<Player>();
    return () =>
    {
        var enemy = Instantiate(enemyPrefab);
        enemy.Construct(player);
        return enemy;
    };
}, Lifetime.Scoped);

其他差异

Zenject VContainer
Signal 不支持
中央消息传递模式(central messaging pattern)很有用,但在很大程度上取决于项目的风格,首选的实现方式会有所不同。 可以选择任何其他实现,比如 VitalRouterCysharp/MessagePipe 等。
Memory Pool(内存池) 不支持
目前,没有嵌入任何内存池的实现。 请根据项目的需求,将实现注入到 Factory 等中。
Container.Bind() .FromComponentInNewPrefabResource("Some/Path/Foo") 不支持
应当使用 LoadAsync 系列方法来加载资源,并在加载资源后使用 RegisterInstance() 等方法进行注册。
Container.Bind() .WithId("foo") .AsCached() 不支持
不推荐重复类型的 Resolve。推荐使用特定类型的 Register,例如 builder.Register(Lifetime.Scoped).WithParameter("foo", foo)。
posted @ 2025-02-18 16:02  凌雪寒  阅读(147)  评论(0)    收藏  举报