使用泛型方法时如何正确约束相关类类型?

问题描述

我有两个基类 BaSEObjectBaSEObjectSettings。第一个定义对象行为,第二个定义类的状态(用于序列化)。

如果我想创建具有特定设置的派生 BaSEObject 类,那么我可以使用具有泛型类型约束的方法

public void CreateBaSEObjectInstance<T>(BaSEObjectSettings baSEObjectSettings) where T : BaSEObject
{
    var instance = pool.GetInstance<T>();
    instance.Settings = baSEObjectSettings;
    scene.Add(instance);
}

我面临的问题是,虽然我可以将泛型类型限制为 BaseClass,但无法将 BaseClassSettings 限制为相关的派生 BaseClass。这意味着我可以做诸如

之类的事情
CreateBaSEObjectInstance<Banana>(new AppleSettings());

这看起来有点可怕。

鉴于我目前被限制在将对象添加到场景之前以相同的方法创建和初始化对象,我有哪些选择?

解决方法

一种方法是让所有设置类都继承自通用基类。通用基类可以从 BaseObjectSettings 继承。泛型类型参数指示此设置类适用于哪种对象。

例如,对于您的 AppleSettings

class AppleSettings: ObjectSettings<Apple> {
    ...
}

abstract class ObjectSettings<T>: BaseObjectSettings where T: BaseObject {}

现在,您可以更改 CreateBaseObjectInstance 以接受 ObjectSettings<T> 的实例:

public void CreateBaseObjectInstance<T>(ObjectSettings<T> objectSettings) where T : BaseObject
{
    var instance = pool.GetInstance<T>();
    instance.Settings = objectSettings;
    scene.Add(instance);
}

如果您将 Banana 作为 T 传递,它会期望 ObjectSettings<Banana>,从而阻止您给它 AppleSettings,即 ObjectSettings<Apple>

,

我不太明白这里的逻辑,因为缺少一些东西,但是从提供的代码中您可能可以编写:

public void CreateBaseObjectInstance<TBase,TSettings>(TSettings baseObjectSettings)
where TBase : BaseObject 
where TSettings : BaseObjectSettings 

这样使用:

CreateBaseObjectInstance<Banana,AppleSettings>(new AppleSettings());

可以改进为:

public void CreateBaseObjectInstance<TBase,TSettings>(TSettings baseObjectSettings)
where TBase : BaseObject 
where TSettings : BaseObjectSettings,new()
{
  if ( baseObjectSettings == null ) baseObjectSettings = new TSettings();
  ...
}

CreateBaseObjectInstance<Banana,AppleSettings>();

但如果实体和设置之间存在强耦合,您应该重新设计以使用与@Sweeper 和@Moho 的答案类似的事物来定义具有关联的依赖关系:

Association,Composition and Aggregation in C#

Understanding the Aggregation,Association,Composition

Generics in .NET

Generic classes and methods

,

您需要创建一个通用接口或基类来定义设置类型:

public class BaseObject<TSettings>
{
    public TSettings Settings { get; set; }
}

那么您的方法将需要两个通用参数 - 一个用于创建 TObject 的实际对象,另一个用于设置 TSettings 的方法参数。然后将 TObject 约束到已实现接口或其基类/派生的实现,使用泛型参数 TSettings 作为约束类型的泛型参数

public void CreateBaseObjectInstance<TObject,TSettings>(
    TSettings settings
)
    where TObject : BaseObject<TSettings>
{
    ...
}

示例(使用上面的 BaseObject 实现):

public class MyObjectSettings
{
    ...
}

public class MyObject : BaseObject<MyObjectSettigns>
{
}

方法调用:

var settings = new MyObjectSettings(){ ... };

CreateBaseObjectInstance<MyObject>( settings );  // second generic argument should be inferred

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...