Home > Mobile >  Permanently downcast a generic in a derived class
Permanently downcast a generic in a derived class

Time:01-28

Ok so the scenario is I have several services that share similar behaviors and all contain settings that are shared across them.

These services have their own extended service specific settings which I am unable to access in the derived service class. Below is essentially an example of what I am trying to do.

public abstract class BaseSettings<T> where T : ISettings
{
  public string BaseSetting { get; } = "Hello from BaseSettings";
  // other settings, etc.

  public BaseSettings<T>() {}
}

public abstract class BaseService<T> where T : ISettings
{
  public BaseSettings<T> Settings { get; }

  public BaseService(BaseSettings<T> settings)
  {
    Settings = settings;
    Console.WriteLine(Settings.BaseSetting);
  }
}

public class ServiceASettings : BaseSettings<ServiceASettings>, ISettings
{
  public string SettingA { get; } = "Hello from SettingsA";

  public ServiceASettings() {}
}

public class ServiceA : BaseService<ServiceASettings>, IService
{
  public ServiceA(ServiceASettings settings) : base(settings) 
  {
   Console.WriteLine(Settings.SettingA); // cant see this property
   Console.WriteLine((Settings as SettingsA).SettingA); // prints fine
  }
}

// ServiceB, ServiceBSettings; ServiceC, ServiceCSettings; etc...

public class Program
{
  public static void Main(string[] args)
  {
    SettingsA settings = new SettingsA();
    ServiceA service = new ServiceA(settings);
    // does not compile because says there is no definition for 
    // Settings.SettingA within ServiceA
  }
}

I cannot figure out how to dynamically downcast(?) the Settings in the derived Service class to access them or essentially make them the appropriate concrete class.

Is there a way to permanently set Settings as the appropriate derived class or do I have to call the specific properties using casting on everything?

ie avoid (Settings as SettingsA).PropertyA; (Settings as SettingsB).PropertyB and so on?

Would appreciate if someone can explain how to accomplish this or if there is an easier way since may be over complicating this.

Any help is appreciated.

Solution

changes made to get it to work

public abstract class BaseSettings
{
  public string BaseSetting { get; } = "Hello from BaseSettings";
  // other settings, etc.

  public BaseSettings() {}
}

public abstract class BaseService<T> where T : ISettings
{
  public T Settings { get; }

  public BaseService(T settings)
  {
    Settings = settings;
    Console.WriteLine(Settings.BaseSetting);
  }
}

public class ServiceASettings : BaseSettings, ISettings
{
  public string SettingA { get; } = "Hello from SettingsA";

  public ServiceASettings() {}
}

Thanks to Ann for the suggestions.

CodePudding user response:

This might be the problem:

You have BaseService.Settings defined this way:

public abstract class BaseService<T> where T : ISettings
{
  public BaseSettings<T> Settings { get; }
}

It should probably be like this instead:

public abstract class BaseService<T> where T : ISettings
{
  public T Settings { get; }
}

The explanation: in your ServiceA, in the original implementation, your Settings property is going to be a BaseSettings<ServiceASettings>, not a ServiceASettings. And BaseSettings<T> never exposes a property of type T, so there's no way from within ServiceA to access a property of type ServiceASettings.

I don't think BaseSettings necessarily needs to take a generic type at all (based on what you've shown us), or if it does, that it needs to take another derivation of ISettings as its generic type, unless you're going to expose an instance of T from it.

There are a couple of other ways to approach the problem, as well.

  • Related