You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Unity客户端与Azure Functions服务端间优雅共享代码?

优雅实现跨端领域类的客户端增强方案

核心目标:在不污染共享领域类库(避免服务端引入Unity依赖)的前提下,让客户端给领域类添加专属增强属性/逻辑,以下是几种实用且优雅的方案:


方案1:对象关联+客户端注册表(推荐轻量场景)

核心思路是把客户端专属数据和共享领域类解耦关联,而非通过继承或泛型强行绑定。共享类库只保留核心字段,客户端用注册表来关联专属资源。

共享类库代码(服务端+客户端共用)

public class DomainBullets
{
    public string Property;
    // 唯一标识用于客户端关联
    public Guid Id { get; } = Guid.NewGuid();
}

public class DomainGun
{
    public int BulletCount;
    public string Name;
    public DomainBullets Bullets;
}

Unity客户端代码(仅存在于Unity项目,不进入共享DLL)

// 客户端专属的渲染器类
public class BulletRenderer
{
    public UnityRenderingThing UnityDependentThing;
}

// 注册表用于关联领域对象和客户端资源
public static class BulletClientRegistry
{
    private static readonly Dictionary<Guid, BulletRenderer> _bulletRendererMap = new();

    public static void RegisterBulletRenderer(DomainBullets bullet, BulletRenderer renderer)
    {
        _bulletRendererMap[bullet.Id] = renderer;
    }

    public static BulletRenderer GetBulletRenderer(DomainBullets bullet)
    {
        _bulletRendererMap.TryGetValue(bullet.Id, out var renderer);
        return renderer;
    }
}

使用方式

// 客户端拿到DomainGun后直接获取渲染器
var bulletRenderer = BulletClientRegistry.GetBulletRenderer(gun.Bullets);
if (bulletRenderer != null)
{
    bulletRenderer.UnityDependentThing.DoRender();
}

优势:共享类库完全无客户端依赖,避免转型操作,代码逻辑清晰,适合轻量扩展场景。


方案2:接口分离+封装增强(适合复杂业务场景)

通过接口定义核心领域契约,客户端在自己的项目中实现增强接口,封装共享领域类,既保留核心数据一致性,又添加专属能力。

共享类库代码

// 核心领域契约,服务端和客户端都依赖
public interface IBulletCore
{
    string Property { get; set; }
}

// 基础实现,服务端直接使用
public class DomainBullets : IBulletCore
{
    public string Property { get; set; }
}

public class DomainGun
{
    public int BulletCount;
    public string Name;
    public IBulletCore Bullets;
}

Unity客户端代码

// 客户端增强接口,仅客户端可见
public interface IClientBullet : IBulletCore
{
    BulletRenderer Renderer { get; set; }
}

// 客户端实现,封装核心领域对象
public class ClientBullet : IClientBullet
{
    private readonly IBulletCore _coreBullet;

    public BulletRenderer Renderer { get; set; }

    // 代理核心属性,保证数据一致性
    public string Property
    {
        get => _coreBullet.Property;
        set => _coreBullet.Property = value;
    }

    public ClientBullet(IBulletCore coreBullet)
    {
        _coreBullet = coreBullet;
    }
}

使用方式

// 客户端从服务端拿到DomainGun后,封装为增强版对象
var clientBullet = new ClientBullet(gun.Bullets);
// 直接访问Renderer属性,无需转型
clientBullet.Renderer = new BulletRenderer();

优势:遵循依赖反转原则,业务逻辑分层清晰,适合需要扩展更多客户端专属行为的复杂场景。


方案3:Unity组件模式(贴合Unity生态)

如果是Unity特有的场景,直接利用Unity的组件系统,把共享领域类和客户端渲染组件分离,符合Unity开发者的惯用思维。

共享类库代码(保持纯净)

public class DomainBullets
{
    public string Property;
}

public class DomainGun
{
    public int BulletCount;
    public string Name;
    public DomainBullets Bullets;
}

Unity客户端组件

public class BulletRendererComponent : MonoBehaviour
{
    // 直接在Inspector中关联共享领域对象
    public DomainBullets BulletData;
    // 客户端专属渲染资源
    public BulletRenderer Renderer;

    private void Start()
    {
        // 初始化时绑定数据和渲染逻辑
        Renderer.SetBulletData(BulletData);
    }
}

优势:完全贴合Unity的开发模式,无需额外写关联逻辑,可视化配置更方便,适合Unity项目的快速迭代。


对原有方案的问题分析

  • 继承方案:强制客户端进行向下转型,不仅代码冗余,还违反了里氏替换原则(客户端子类添加了服务端不需要的属性,破坏了父类的契约纯净性)。
  • 泛型空接口方案:空接口属于代码异味,本质是用泛型强行“占位”,既增加了共享类库的复杂度,又没有实际的契约约束,后期维护成本高。

内容的提问来源于stack exchange,提问作者user3010678

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 00:20:38