Binding with Zenjection - c#

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

Related

Permanently downcast a generic in a derived class

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.

Calling abstract method in constructor of abstract class

As CA2214 states, one should not call an overridable method in a constructor. However, I've come across a case where I can't see another way to do what I'm trying to achieve and I can't see potential problems arising from breaking this rule:
I have an abstract base class for configurations. In this class, there is logic for how to fetch the values.
My applications have configs that can be made up of certain components. So my SpecificConfig would inherit from ConfigBase and be made up of ConfigComponentA and ConfigComponentB:
public abstract class ConfigBase
{
protected ConfigBase()
{
this.InitializeMembers();
this.SetConfigValues();
}
protected abstract void InitializeMembers();
private void SetConfigValues() {
// Set the config values
// Depends on members initialized in InitializeMembers
}
}
public class ConfigComponentA
{
public string FieldA1;
public string FieldA2;
}
public class ConfigComponentB
{
public string FieldB1;
public string FieldB2;
}
public sealed class SpecificConfig : ConfigBase
public SpecificConfig() : base() {}
public ConfigComponentA ConfigA;
public ConfigComponentB ConfigB;
protected override void InitializeMembers()
{
this.ConfigA = new ConfigComponentA();
this.ConfigB = new ConfigComponentB();
}
}
The main point is that the configs could be made up of different components, and I want to avoid code duplication by having the logic for fetching and setting the config values in SetConfigValues() in the base class.
I have a feeling there may be a better way of going about this altogether, but I don't really see any unexpected behaviour that could come of this. Is there a better approach?

Overloading methods while using Generics with base classes

I have the following Classes:
public abstract class Gear<T> : ScriptableObject, IGear { ... }
public class Armor : Gear<ArmorStatsLevel> { ... }
public class Weapon : Gear<WeaponStatsLevel> { ... }
Now I had the following methods to list my instances:
public abstract class WidgetListArmor {
public void ActionSelected(Armor gear) {
if (...) GameSession.Equip(gear);
}
}
public abstract class WidgetListWeapon {
public void ActionSelected(Weapon gear) {
if (...) GameSession.Equip(gear);
}
}
Because this was kind of redundant, I thought of moving it all to a base clase:
public abstract class WidgetListGear<T> : MonoBehaviour {
public void ActionSelected(T gear) {
if (...) GameSession.Equip(gear);
}
}
public class WidgetListArmors : WidgetListGear<Armor> { ... }
public class WidgetListWeapons : WidgetListGear<Weapon> { ... }
And while this seems cleaner, I have a new problem now. Because T is a Generic, GameSession.Equip can't overload gear.
Did I chose a bad pattern to organize my code? Am I missing something from Generics that allows me to do this operation?
UPDATE
Here is the GameSession signatures:
public class GameSession {
public static bool Equip(Armor armor);
public static bool Equip(Weapon weapon);
}
Make Weapon and Armor implement an interface called IGear, for example:
public interface IGear
{ }
public class Weapon : IGear
{
//snip
}
public class Armor : IGear
{
//snip
}
Constrain the generic type to IGear:
public abstract class WidgetListGear<T> : MonoBehaviour
where T : IGear
{
public void ActionSelected(T gear) {
if (...) GameSession.Equip(gear);
}
}
And make GameSession.Equip take IGear as the parameter type.
What you're looking for is dynamic dispatch. I would suggest you try the following:
GameSession.Equip((dynamic)gear);
However, I don't think it's the best idea since you've tried to encode your Game rules in type system and right now you're starting a mini-compiler in runtime to perform a dispatch for you.
I'd like to point you to Eric Lippert's articles on that subject. Looks like you have similar issues with what he's described.
Part 4 describes the dynamic approach I've provided as well as its disadvantages. Part 5 provides a completely different approach. Overall, I highly recommend reading each part.

Declaring Method with generic type

I was using generic types in C# and I am new to using generic types. So, right now I am stuck with a problem. I have some classes like these:
public class MyModel1
{
}
public class MyModel2
{
}
public class BaseClass<T>
{
}
public class ChildClass1 : BaseClass<MyModel1>
{
}
public class ChildClass2 : BaseClass<MyModel2>
{
}
public class AnotherClass
{
//What will be the syntax of declaring this method
//The syntax of the following method is wrong and incomplete.
//It's there just to give an idea about whai i want to do.
public void MyMethod<T>()
where T : BaseClass<..what to write..>
{
}
}
My question is what will be the correct syntax of declaring MyMethod if I want to call MyMethod like this:
MyMethod<ChildClass1>();
If I understood correctly, you try to filter "MyMethod" so that T is a class of type "ChildClass ...".
You can add a generic parameter to your function like this:
public void MyMethod<T, U>()
where T : BaseClass<U>
{
}
But then you have to call MyMethod in that way.
MyMethod<ChildClass1, MyModel1>();
So it's quite complicated to use.
Another solution is to create a new "blank" class :
public abstract class Base // mark it as abstract if you don't need to use it in your code
{
}
public class MyModel1
{
}
public class MyModel2
{
}
public class BaseClass<T> : Base //The class inherits the new class
{
}
public class ChildClass1 : BaseClass<MyModel1>
{
}
public class ChildClass2 : BaseClass<MyModel2>
{
}
public class AnotherClass
{
public void MyMethod<T>()
where T : Base
{
}
}
You've forgotten to mention the return type and adding <T> after the class name. For example, if the return type is void, you could declare the method as:
public void MyMethod<T>()
where T : BaseClass<T>
{
}
This will work (by which I mean it compiles)
public void MyMethod<T>()
where T : BaseClass<MyModel1>
{ }
so does this:
public void MyMethod<T>()
where T : ChildClass1
{ }
Further edit after reading your comment...
You can do this:
public class AnotherClass<TBaseClass, TModel> where TBaseClass : BaseClass<TModel>
{
public void MyMethod(TBaseClass input)
{ }
}
I have a term for this, hopefully non-offensive. I call it The Generic Rabbit Hole of Madness. It's what happens when we try to combine generics and inheritance so that one set of classes can accomplish a broad set of goals that become increasingly confusing, and we solve it by adding more generic parameters and more generic classes.
You reach the bottom of the hole if you
- use <dynamic>
- check to see what the actual type is using GetType(), typeof, or is
- get it to compile but can't remember what it's supposed to do

Share functionality between two classes

This question sounds very common, but I have no idea how i should implement it.
My scenario:
Question: How to implement DirectionTile?
Behavior implementation of DirectionTile:
abstract class DirectionTile
{
public Point Direction;
public Tile Next => Board.GetTile(Coords + Direction);
}
As you can see property Coords will be from the base class (?), which is Tile.
Both RailTile and SailTile are DirectionTiles. However, I can not just inherit from DirectionTile, as SailTile needs to inherit from WaterTile and this would case 2 relations with Tile (really want to use polymorphism like this).
I hope somebody can help me with this problem, that I can not solve. Let me know if my explanation is clear enough.
If i understand correctly, DirectionTile is a behaviour implemented by entity.
Use simply an Interface IDirectionTile, and to prevent multiple implementation you could provide an extension Methods :
public interface IDirectionTile
{
}
public static class TileExtensions
{
public static void GetDirection(this IDirectionTile th)
{
}
public static void Next(this IDirectionTile th)
{
}
}
Do it like this: Base from your diagram Tile will be your base/parent class and all other classes will be its child class. which then will inherit the property and function(s) that the Tile has. For DirectionTile i think it's a separate entity/class where you will just instantiate WaterTile and RailTile
public abstract class Tile
{
private Point direction;
public Point Direction
{
get { return direction; }
set { direction = value; }
}
public abstract string someFunction();
}
Now to inherit the property and the function:
public class WaterTile : Tile
{
//all property and functions now are accessible from the Tile base class
private string somevariable;
public string Somevariable
{
get{return somevariable;}
set{somevariable=value;}
}
public override string someFunction()
{
//return "something";
}
}
public class SailTile : WaterTile
{ //if there are additional properties/functions in your WaterTile it will be accessible by this class (SailTile) but not to other class
//in this case Somevariable is accessible in this class
}

Categories