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

How do I correctly constrain to related class type when using a generic method?

我有两个碱基 classes BaseObjectBaseObjectSettings。第一个定义对象行为,第二个定义 class 的状态(对序列化有用)。

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

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());

这看起来有点可怕。

考虑到我目前只能在将对象添加到场景之前使用相同的方法创建和初始化对象,我有哪些选择?

我不太理解这里的逻辑,因为缺少一些东西,但是从提供的代码中你可能会写:

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#

Generics in .NET

Generic classes and methods

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

例如,对于您的 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>

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

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

那么您的方法将需要两个通用参数 - 一个用于要创建的实际对象 TObject,另一个用于设置 TSettings 的方法参数。然后,您将 TObject 约束为已实现接口的实现或其基础 class/derivation,使用泛型参数 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