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

Unity泛型类约束为多派生类的实现方案问询

Unity ScriptableObject泛型约束:类型参数二选一的实现方案

首先明确:C#泛型约束不支持直接指定“或”逻辑,你当前写的where T1 : ClassA OR ClassB语法不存在,编译器会将多类型约束解析为“同时满足”,这也是Rider提示冲突的原因。

原方案是否可行?

不可行。C#泛型约束规则中,类类型约束只能指定一个基类(因为C#不支持多继承),无法同时指定两个不同的类作为可选约束。

替代方案(不破坏现有系统)

方案1:利用基类+特性标记,运行时/编译时校验

既然ClassA和ClassB都继承自BaseClass,可以先将约束设为BaseClass,再通过自定义特性标记允许的类型,配合代码分析器或运行时检查来限制T1的范围。

步骤:

  1. 定义自定义特性:
[AttributeUsage(AttributeTargets.GenericParameter)]
public class AllowedScriptableTypeAttribute : Attribute
{
    public Type[] AllowedTypes { get; }

    public AllowedScriptableTypeAttribute(params Type[] allowedTypes)
    {
        AllowedTypes = allowedTypes;
    }
}
  1. 修改NewClass的泛型约束,并添加特性:
public class NewClass<T1> 
    where T1 : BaseClass
{
    static NewClass()
    {
        // 运行时校验,可选
        var genericParam = typeof(NewClass<T1>).GetGenericArguments()[0];
        var allowedTypes = new[] { typeof(ClassA), typeof(ClassB<>).GetGenericTypeDefinition() };
        
        // 处理泛型ClassB的情况
        bool isAllowed = genericParam == typeof(ClassA) || 
                        (genericParam.IsGenericType && genericParam.GetGenericTypeDefinition() == typeof(ClassB<>));
        
        if (!isAllowed)
        {
            throw new InvalidOperationException($"类型 {genericParam.Name} 不允许作为NewClass的泛型参数");
        }
    }
}
  1. 使用示例:
// 合法用法
public class UsageA : NewClass<ClassA> {}
public class UsageB : NewClass<ClassB<int>> {}

// 非法用法(运行时会抛出异常)
public class UsageC : NewClass<OtherDerivedFromBase> {}

方案2:拆分泛型类为两个版本

如果需要编译时严格约束,无需运行时检查,可以将NewClass拆分为两个针对不同类型的泛型类,再通过抽象基类统一接口:

// 统一抽象基类
public abstract class NewClassBase {}

// 针对ClassA的版本
public class NewClassForA<T1> : NewClassBase where T1 : ClassA {}

// 针对ClassB的版本
public class NewClassForB<T1, T2> : NewClassBase where T1 : ClassB<T2> {}

这种方式完全在编译时约束类型,不会引入运行时开销,但需要维护两个类。

方案3:泛型工厂方法重载

如果NewClass的实例化可以通过工厂方法完成,可通过重载工厂方法来实现类型约束的“二选一”:

public class NewClass<T> where T : BaseClass
{
    private NewClass(T instance)
    {
        // 核心逻辑实现
    }

    // 针对ClassA的工厂方法
    public static NewClass<T> Create<T>(T instance) where T : ClassA
    {
        return new NewClass<T>(instance);
    }

    // 针对ClassB的工厂方法
    public static NewClass<T> Create<T, U>(T instance) where T : ClassB<U>
    {
        return new NewClass<T>(instance);
    }
}

调用时编译器会根据传入的类型自动匹配对应的约束,达到预期的类型限制效果。

总结

如果不想修改现有ClassA、ClassB的结构,优先选择方案1;如果需要编译时严格校验且能接受类拆分,方案2更合适;若项目依赖工厂模式实例化对象,方案3是简洁的选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 08:10:25