Unity泛型类约束为多派生类的实现方案问询
Unity ScriptableObject泛型约束:类型参数二选一的实现方案
首先明确:C#泛型约束不支持直接指定“或”逻辑,你当前写的where T1 : ClassA OR ClassB语法不存在,编译器会将多类型约束解析为“同时满足”,这也是Rider提示冲突的原因。
原方案是否可行?
不可行。C#泛型约束规则中,类类型约束只能指定一个基类(因为C#不支持多继承),无法同时指定两个不同的类作为可选约束。
替代方案(不破坏现有系统)
方案1:利用基类+特性标记,运行时/编译时校验
既然ClassA和ClassB都继承自BaseClass,可以先将约束设为BaseClass,再通过自定义特性标记允许的类型,配合代码分析器或运行时检查来限制T1的范围。
步骤:
- 定义自定义特性:
[AttributeUsage(AttributeTargets.GenericParameter)] public class AllowedScriptableTypeAttribute : Attribute { public Type[] AllowedTypes { get; } public AllowedScriptableTypeAttribute(params Type[] allowedTypes) { AllowedTypes = allowedTypes; } }
- 修改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的泛型参数"); } } }
- 使用示例:
// 合法用法 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
相关产品推荐
相关产品推荐

