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.
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
I think it's better to formulate the problem via code. I have a BaseClass.
public abstract class BaseUnit {
[System.Serializable]
public class Settings
{
}
}
And some derived classes, for example.
public class Archer : BaseUnit {
public ArcherSettings Settings;
[System.Serializable]
public class ArcherSettings : Settings
{
//CanWalk is a MonoBehaviour and WalkSettings is a Serrializable class
public CanWalk.WalkSettings WalkSettings;
}
}
So as you can see I want to have several unit types with appropriate WalkSettings which will be set from ScriptableObject.
public class ScriptableLevelInstaller : ScriptableObjectInstaller<ScriptableLevelInstaller>
{
public Archer.AracherSettings Aracher;
public Knight.KnightSettings Knight;
//Some more...
}
So the question is how to Inject appropriate settings into appropriate classes with Zenject any help or clarification would be very helpful.
---UPD---
I express myself poorly the first time.
What I want is bind CanWalk.WalkSetting to approprirate settings.
So I can do
Container.Bind<CanWalk.WalkSettings>().FromInstance(Archer.WalkSettings);
But this is wrong because the last binding will just override walk settings for every class.
So What I need is something like
Container.Bind<CanWalk.WalkSettings>().FromInstance(Archer.WalkSettings).WhenInjectInto("CanWalk which is attached to an Archer")
For now I'm just doing this inside Aracher.
GetComponent<CanWalk>().Settings = _settings.WalkSettings;
But maybe there is something in Zenject to solve this.
Just use Container.BindInstance like this:
public class ScriptableLevelInstaller : ScriptableObjectInstaller<ScriptableLevelInstaller>
{
public Archer.AracherSettings Aracher;
public Knight.KnightSettings Knight;
public override void InstallBindings()
{
Container.BindInstance(Aracher);
Container.BindInstance(Knight);
}
}
If you want you can also specify the class that should get access to it like this:
public class ScriptableLevelInstaller : ScriptableObjectInstaller<ScriptableLevelInstaller>
{
public Archer.AracherSettings Aracher;
public Knight.KnightSettings Knight;
public override void InstallBindings()
{
Container.BindInstance(Aracher).WhenInjectedInto<Archer>();
Container.BindInstance(Knight).WhenInjectedInto<Knight>();
}
}
But this is not necessary, which is why I tend to use the first approach
I've created a plugin system within my code, which loads types from DLLS. I grab the type I want from the loaded DLL using this code;
var type = Assembly.LoadFrom(filePath).GetTypes()
.FirstOrDefault(t =>
t.IsClass && t.IsSubclassOfRawGeneric(typeof(DespatchBasePlugin<>)));
IsSubClassOfRawGeneric hunts down the base type as it is buried several classes down, the code works and the correct type is returned.
I then create an instance of this class using Activator;
DespatchBasePlugin<XMLSettingBase> obj = Activator.CreateInstance(type, new object[] { logger }) as DespatchBasePlugin<XMLSettingBase>;
Unfortunately the cast on this line creates a null reference. Removing the cast returns an instance of the class in question, but I need to store is as its base type.
This is the class being loaded(Shortened for brevity);
public class DHLPlugin : DespatchBasePlugin<UserSetting>
{
public DHLPlugin(BaseForm logger) : base("DHL", logger)
{
this.order = 10;
}
}
And this is the base class I want it to use(Note the class itself has a base class, it goes several layers deep);
public abstract class DespatchBasePlugin<TSettings> : DespatchBase<TSettings> where TSettings : XMLSettingBase, new()
The previous code used a base class with no generic assigned to it and worked absolutely fine. It looked like this;
DespatchBasePlugin obj = Activator.CreateInstance(type, new object[] { logger }) as DespatchBasePlugin;
I'm sure I'm doing something dumb, please tell me what it is.
Edit - Not marked this as duplicate as I believe this is a better question/answer than the other which consists of a generic link to MSDN as the answer. If this is not a correct way to use the duplicate system please let me know.
You can use contravariance to define your plugin:
public class Program
{
public static void Main()
{
var settings = new DerivedSettings()
{Name = "John"};
DerivedPlugin a = new DerivedPlugin(settings);
IPlugin<BaseSettings> sample = (IPlugin<BaseSettings>)a;
Console.WriteLine(sample.GetName());
}
}
public abstract class BaseSettings
{
public abstract string Name
{
get;
set;
}
}
public interface IPlugin<out TSettings>
where TSettings : BaseSettings
{
string GetName();
}
public abstract class BasePlugin<TSettings> : IPlugin<TSettings> where TSettings : BaseSettings
{
protected readonly TSettings _settings;
public BasePlugin(TSettings settings)
{
_settings = settings;
}
public virtual string GetName()
{
return _settings.Name;
}
}
public class DerivedSettings : BaseSettings
{
public override string Name
{
get;
set;
}
}
public class DerivedPlugin : BasePlugin<DerivedSettings>
{
public DerivedPlugin(DerivedSettings settings): base (settings)
{
}
}
I've included a BasePlugin class, but this is optional and you can just directly use the interface.
I have an abstract base configuration class and two implementations:
public abstract class BaseConfiguration
{
}
public class LoginConfiguration : BaseConfiguration
{
public LoginConfiguration()
{
}
public string Name { get; set; }
public string Password { get; set; }
}
public class TestConfiguration : BaseConfiguration
{
public TestConfiguration()
{
}
}
The problem I am facing:
Every specific class type has a explicit filename it points to. This means LoginConfiguration has a filename called "login.xml" and TestConfiguration points to "test.xml".
The filename I would like to use for deserialization later on:
static void Main(string[] args)
{
LoginConfiguration login = ReadFromFile<LoginConfiguration>();
Console.ReadLine();
}
private static TConfig ReadFromFile<TConfig>() where TConfig : BaseConfiguration
{
//Something like this needs to be done here:
string filename = TConfig.GetFilename();
//Deserialize file and return object
return Deserialize<TConfig>(filename);
}
But I know that you can neither have static overides nor static abstract methods.
What I am currently doing is using the base class to instantiate a new object and read the filename from the instance, but thats very hacky.
public abstract class BaseConfiguration
{
protected BaseConfiguration(string fileName)
{
Filename = fileName;
}
public string Filename { get; private set; }
public static string GetFilename<TConfig>() where TConfig : BaseConfiguration, new()
{
return new TConfig().Filename;
}
}
//The calling method:
private static TConfig ReadFromFile<TConfig>() where TConfig : BaseConfiguration, new()
{
string filename = BaseConfiguration.GetFilename<TConfig>();
//Deserialize file and return object
return Deserialize<TConfig>(filename);
}
My question is now:
Do you have any idea, how I can design it better? Do you have a better Approach?
And wouldn't static abstract methods in C# make sense at this kind of issue?
Thanks in advance!
If you don't mind a bit of reflection, you could add an attribute to provide the file name:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ConfigFileAttribute : Attribute
{
...
}
[ConfigFile("login.xml")]
public class LoginConfiguration
{
...
}
Since you already have the type (TConfig), you can access the attribute using:
var configAttributes = typeof(TConfig).GetCustomAttributes(typeof(ConfigFileAttribute), false);
Of course this does mean that forgetting the attribute would make the read fail.
This is not the "best" solution, but I personally prefer the separate administration of a filename, where the class itself has no interest in it.
The reason for your weird design is because your XXXConfiguration class breaks Single Responsibility Principle, it has a property named filename, to tell the caller where its data comes from, and other properties to save the loaded data. IMO the XXXConfiguration class should only contain the data, there is no information of its data source.
abstract class BaseConfiguration
{
public string SharedConfigProperty { get; set; }
}
class LoginConfiguration : BaseConfiguration
{
public string LoginConfigProperty { get; set; }
}
class TestConfiguration : BaseConfiguration
{
public string TestConfigProperty { get; set; }
}
In your original design, LoginConfiguration can only have one data-source, it will be a nightmare if you have login-dev.xml login-qa.xml in the future. Put the load function of configurations in another class is better:
class ConfigurationFactory
{
public static TConfig FromFile<TConfig>() where TConfig : BaseConfiguration
{
//you should have a TConfig-fileName mapping
//e.g. a Dictionary<Type, string>
//Type is typeof(TConfig) and string is the filename
}
public static TConfig FromDataBase<TConfig>() where TConfig : BaseConfiguration
{
//as I said, the original design has a lot of restricts
//what if they change the storage from file to data base?
//you need to change every derived class, renaming FileName to DataBaseTableName?
}
}
Yeah I sometimes wish for the ability to specify a contract 'if you extend this class then you need to provide a static method with this signature', but unfortunately that doesn't exist.
It's not that much neater, but I would probably make a static map of the filenames like this:
public abstract class BaseConfiguration
{
static BaseConfiguration()
{
Filenames = new Dictionary<Type, string>
{
{ typeof(LoginConfiguration), "login.xml" },
{ typeof(TestConfiguration), "test.xml" },
};
}
private static Dictionary<Type, string> Filenames { get; }
public static string GetFilename<TConfig>() where TConfig : BaseConfiguration
{
return Filenames[typeof(TConfig)];
}
}
The advantages being that you have all the filenames in one place and that you don't need to allocate an instance to get the filename.
I have a property A in all subclasses of base class Base.
How can I generate an abstract property definition of property A into base class Base?
I know ReSharper's refactoring Pull Members Up, but that moves the property to base class.
I need an abstract property in base class and a overriding properties in all sub classes. Is there a refactoring in Visual Studio or in ReSharper that can do it automatically for me?
There is a checkbox "Make abstract" for that in ReSharper Pull Members Up dialog :
I'm not sure Resharper can move up and create an abstraction as you want automatically, but you can atleast define it manually like this
In abstract class:
public abstract double A
{
get;
}
In Sub class:
public override double A
{
get
{
return 3.141;
}
}
It might be a clearner design to define a new Interface (or use an existing one) and define the property in the interface. That way, your existing subclasses won't have to use override.
public interface IInterface {
string MyProperty { get; }
}
public class Class : IInterface {
public string MyProperty { get; set; }
}
public abstract class AbstractClass {
public abstract string Value { get; }
}
public class ConcreteClass : AbstractClass {
private string m_Value;
public override string Value {
get { return m_Value; }
}
public void SetValue(string value) {
m_Value = value;
}
}
I hope this will be helpful to you.
I have a base class that takes a single generic argument. I then have several classes that inherit from this base class. Is there a simple way for the child classes to inherent a factory from the base class?
Example
class BaseClass<T>
{
T Value {get; set;}
string Name {get; set;}
public static BaseClass<T> Factory(T Value)
{
return new BaseClass<T>(Value);
}
}
class ChildClass : BaseClass<int>
{
public void Test()
{
// I want this below to work
// but Factory() returns a BaseClass
ChildClass bs = ChildClass.Factory(10);
}
}
I've noted in the code what I want to work. I can think of one way to overcome this, by adding an implicit operator to either BaseClass or SubClass that converts from BaseClass to ChildClass.
I can also just explicitly add the Factory to ChildClass but that defeats the point of inheritance.
Is there a better, more standardized way of doing this?
I would do something like this:
class BaseClass<T, K> where K : BaseClass<T, K>, new()
{
T Value { get; set; }
string Name { get; set; }
public static K Factory(T value)
{
return new K { Value = value };
}
}
class ChildClass : BaseClass<int, ChildClass>
{
public void Test()
{
ChildClass cs = Factory(10);
}
}
It's a bit hard to answer your question since you have described what you are trying to do, but not why. Hence I got to try to guess what you want.
I would not put the factory method in the same class as in the other answer or your question. How would you handle inheritance for once? It works for the two levels that you have. But what if you want to extend ChildClass?
Instead I would create a generic factory used for the object creation. Implement it has a singleton wrapped around a factory interface to be able to easy extend it or swap the implementation.
class MyFactory
{
private static IMyFactory _instance;
public static void Assign(IMyFactory factory) { _instance = factory; }
public static T Create<T>() { return _instance.Create<T>(); }
}
interface IMyFactory
{
T Create<T>();
}
class MyFactoryImp : IMyFactory
{
//do whatever needed in here
public T Create<T>(){ return new T(); }
}
class BaseClass<T>
{
T Value {get; set;}
string Name {get; set;}
}
class ChildClass : BaseClass<int>
{
public void Test()
{
ChildClass bs = MyFactory.Create<ChildClass>(10);
}
}
// start with this, you can easily switch implementation
MyFactory.Assign(new MyFactoryImp());
The other obvious answer would be to start using a Inversion Of Control container, for example autofac.