简体   繁体   中英

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

I have two base classes BaseObject and BaseObjectSettings . The first defines the object behaviour and the second defines the state of the class (useful for serialisation).

If I want to create a derived BaseObject class with specific settings then I can use a method with a generic type constraint.

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

The problem I am facing is that while I can constrain the generic type to BaseClass I can't constrain the BaseClassSettings to the relevant derived BaseClass. This means that I can do things like

CreateBaseObjectInstance<Banana>(new AppleSettings());

which seems a bit terrible.

What are my options given that I am currently constrained to both creating and initialising the object in the same method before adding it to the scene?

One way is to have all your settings classes inherit from a generic base class. The generic base class could then inherit from BaseObjectSettings . The generic type parameter indicates what kind of object this settings class is for.

For example, for your AppleSettings ,

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

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

Now, you can change CreateBaseObjectInstance to accept an instance of ObjectSettings<T> instead:

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

If you pass Banana as T , it would expect ObjectSettings<Banana> , preventing you from giving it AppleSettings , which is ObjectSettings<Apple> .

I don't really understand the logic here as things are missing, but from the code provided you can probably write:

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

Used like that:

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

Can be improved to:

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

CreateBaseObjectInstance<Banana, AppleSettings>();

But if there is a strong coupling between entity and settings, you should redesign to define dependency with an association using a thing that can also be similar to @Sweeper's and @Moho's answers:

Association, Composition and Aggregation in C#

Understanding the Aggregation, Association, Composition

Generics in .NET

Generic classes and methods

You need to create a generic interface or base class that where you define the settings type:

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

Then your method will require two generic arguments - one for the actual object to create TObject and one for method's argument for the settings TSettings . You then constrain TObject to an implementation of the implemented interface or base class/derivation thereof, using generic argument TSettings as the constraint's type's generic argument

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

Example (using above BaseObject implementation):

public class MyObjectSettings
{
    ...
}

public class MyObject : BaseObject<MyObjectSettigns>
{
}

Method call:

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM