Zenject binding multiple components from instatiated prefab - c#

I ran into the problem of binding components from a child component, a solution to this problem is possible?
At the moment I have a solution, but it is inconvenient, you need to forward all methods through 1 component. Example:
public inteface ITextView
{
void SetText(string text);
}
public ButtonView : Monobehaviour, ITextView
{
[SerializedField] private Text _text;
public void SetText(string text)
{
_text.text = text;
}
}
public SomeWindow : Monobehaviour, IButtonView
{
[SerializedField] private TextView _textView;
public void SetText(string text) => _textView.SetText(text);
}
In this case, with the growth of inherited interfaces, the forwarding of methods grows.
As a possible solution to the problem, it is also possible to simply create an interface that stores references to all dependencies. Example:
public interface ISomeWindowFacade
{
ITextView TextView { get; }
//Some dependence
//Another one
}
But in this case, I will pass unnecessary dependencies to most classes
Is it possible to store links to the required dependencies in SomeWindow and bind the rest after its creation?
public Installer : ScriptableObjectInstaller
{
[SerializedField] private SomeWindow _window;
public override void InstallBindings()
{
Container.BindInterfacesTo<ISomeWindowFacade>.FromComponentInNewPrefab(_window).AsSingle();
}
}

Perhaps I did not state the problem correctly, but here's the gist. There is a SOInstaller that stores a link to the prefab, at the time of the bind I created an instance of this component and wanted to receive the component itself and its children as separate components. I found solutions to this problem:
The implementation of the component must store the MonoBehaviour list, you can make an abstract class that stores a link to the list
public interface IWindowView{}
public ConcreteWindow : MonoBehaviour, IWindowView
{
[SerializedField] private List<MonoBehaviour> _children;
public List<MonoBehavior> Children => _children;
}
In the installer (as in my case in SOInstaller) we add a link to the prefab with the component and instantiate the component and bind it
public class SomeInstaller : ScriptableObjectInstaller
{
[SerializeField] private ConcreteWindow _window;
public override InstallBindings()
{
//If you use an abstract class, you can convert to it
var window = Container.InstatiatePrefabForComponent<ConcreteWindow>(_window);
Container.BindInterfacesTo(window.GetType()).AsSingle();
}
}
Now we can go through all the child elements, inject them and bind
public class SomeInstaller : ScriptableObjectInstaller
{
[SerializeField] private ConcreteWindow _window;
public override InstallBindings()
{
//Previous
foreach(var component in window.Children)
{
Container.Inject(component);
Container.BindInterfacesTo(component.GetType()).AsSingle();
}
}
}
As a result, we get the following, we have created an object that stores the components and can receive them separately in the desired class
public class SomeClass
{
//ISomeView - child implementer
public SomeClass(IWindowView windowView, ISomeView someView)
{
}
}

Related

Is there a way to accept unknown types in runtime?

I am trying to solve a simple OOPs problem. When I have to create few weapons and each weapon has a primary action and that primary action can be performed by a mouse click. Example for a shotgun it is shooting and for Katana is swinging it. Below my classes are.
public interface IShootable
{
void TakeShot();
}
public interface ISwingable
{
void Swing ();
}
public class ShotGun : IShootable
{
public void TakeShot()
{
}
}
public class Kanata : ISwingable
{
public void Swing ()
{
}
}
Each weapon has implemented different interface for their primary actions. (I'm not sure that I can create an abstract class, from which I can inherit these concrete classes. It seems not substitutable for two different type of weapons.)
What I wanted to achieve is in runtime when user selects one of the weapons, the user gets the right action on mouse click. For shotgun it is TakeShot() and for Katana it is Swing().
What I have to do adopt that. Or I should restructure the classes in some other way.
Both classes can both implement a third interface - IWeapon, with an Attack/Use method:
public interface IWeapon {
void Attack();
}
public class ShotGun : IShootable
{
void IWeapon.Attack() {
TakeShot();
}
public void TakeShot()
{
}
}
public class Kanata : ISwingable
{
public void Swing ()
{
}
void IWeapon.Attack() {
Swing();
}
}
Note that I have explicitly implemented the IWeapon interface here. You don't have to do it explicitly, and can still do it the "normal way". but I prefer it this way. This way, when you have a Katana object, only Swing is visible, but not Attack. I just feel that having both methods visible is confusing.
Also note that IWeapon is unrelated to ISwingable and IShootable, because swingable things (tennis racket) and shootable things (water pistol) are not necessarily weapons.
You might want to do something more generic:
public interface IWeaponPrimaryAction
{
void PerformPrimaryAction();
}
public interface IWeaponAction
{
void PrimaryAction();
}
public class Shoot :IWeaponAction
{
public void PrimaryAction()
{
//Fire in the hole
}
}
public class Swing :IWeaponAction
{
public void PrimaryAction()
{
//Into pieces
}
}
public class ShotGun : IWeaponPrimaryAction
{
private IWeaponAction _action = new Shoot();
public void PerformPrimaryAction()
{
_action.PrimaryAction();
}
}
public class Kanata : IWeaponPrimaryAction
{
private IWeaponAction _action = new Swing();
public void PerformPrimaryAction()
{
_action.PrimaryAction();
}
}
Better than that just inject the action:
public class ShotGun : IWeaponPrimaryAction
{
private IWeaponAction _action;
public ShotGun(IWeaponAction action)
{
_action = action;
}
public void PerformPrimaryAction()
{
_action.PrimaryAction();
}
}
My preference would be what #Riki suggests and create an interface that all weapons inherit IWeapon with a single method IWeapon.PerformPrimaryAction(). However if you don't like that you could create a "base" interface that all weapon interfaces derive from, then at runtime use casting, is or as to determine which weapon you have:
public interface IWeapon {};
public interface IShootable : IWeapon
{
void TakeShot();
}
public interface ISwingable : IWeapon
{
void Swing ();
}
public partial class YourGameClass
{
public void DoTheAction (IWeapon weapon)
{
if (weapon is IShootable)
(weapon as IShootable).TakeShot();
if (weapon is ISwingable)
(weapon as ISwingable).Swing();
}
}

Auto casting abstracted classes in c#

I'm trying to get my head around making a simplified way of making items, lists and databases in C# And while everything works, It involved me needing to cast the results out.
So far I have the following
namespace Game.Database
{
public abstract class DatabaseItem : ScriptableObject
{
}
public class DatabaseList : ScriptableObject
{
public List<DatabaseItem> items;
}
public class ShipClass : ScriptableObject
{
public string shipClassID;
new public string name;
}
public class Ship : DatabaseItem
{
public string shipID;
new public string name;
public ScriptableObject shipClass;
}
}
public class Database : MonoBehaviour
{
public List<DatabaseList> lists;
void Start()
{
Ship ship = (Ship)lists[0].items[0];
Debug.Log(shipClass.shipID);
ShipClass shipClass = (ShipClass)ship.shipClass;
Debug.Log(shipClass.shipClassID);
}
}
Bear in mind this is a unity project so these items are being instantiated and data being assigned through the UI.
As you can see I have an abstract for my items and will have multiple types of item, and multiple lists. I am trying to avoid having to make multiple class' for my lists, one for each type of item. So i have abstracted my items off DatabaseItem so that I can store a List of DatabaseItem in my DatabaseList. However this means when reading my data out i need to cast this back into a Ship class.
While this isn't bad for a simple implementation, in production these will be nested requiring multiple casts to get down to the required data.
Unfortunately I find myself lacking in the required c# vocabulary to really google the issue. Looking at the Microsoft User-defined conversion operators and their example just doesn't make sense if it's even what i want to achieve.
EDIT -
The issue if not accessing the data, as I can do that, it's having to break down every level of the data as in the end this will be very generic used for all game data and very nested, so having to cast every level out to be able to break it down is what I'm trying to avoid.
One way would be to expose a method on each type of item that does the writing out of the data, so the calling code doesn't need to know the low level details.
See below, on how to avoid doing any casting.
namespace Game.Database
{
public abstract class DatabaseItem : ScriptableObject
{
public abstract void WriteOut();
}
public class DatabaseList : ScriptableObject
{
public List<DatabaseItem> items;
}
public class Ship : DatabaseItem
{
public string shipID;
new public string name;
public override void WriteOut()
{
Debug.Log(shipID);
}
}
}
public class Database : MonoBehaviour
{
public List<DatabaseList> lists;
void Start()
{
lists[0].items[0].WriteOut();
}
}
This way you are allowing each item type to handle its own writing out. Id suggest thinking carefully about your API.
To be even more SOLID and clean, you could use dependency injection and inject the writing into the type, see below for another example.
This has the benefit of allowing multiple types to use the same writer code and you also keep your class following the single responsibility principle.
namespace Game.Database
{
public interface IWriter
{
void Write(string output);
}
public class ConsoleWriter: IWriter
{
public void Write(string output)
{
Debug.Log(output);
}
}
public abstract class DatabaseItem : ScriptableObject
{
public abstract void WriteOut();
}
public class DatabaseList : ScriptableObject
{
public List<DatabaseItem> items;
}
public class Ship : DatabaseItem
{
private IWriter _writer;
public Ship(IWriter writer)
{
_writer = writer;
}
public string shipID;
new public string name;
public override void WriteOut()
{
_writer.Write(shipID);
}
}
}
public class Database : MonoBehaviour
{
public List<DatabaseList> lists;
void Start()
{
lists[0].items[0].WriteOut();
}
}
Something like this should be a good starting point. The specific implementations need to be adapted to your game's needs. More informations about generic types here.
public abstract class Item: ScriptableObject
{
public string name;
public abstract void Use();
}
public class Ship: Item
{
public string id;
public override void Use()
{
Debug.Log($"I'm a Ship, my name is {name}, my id is {id}.");
}
}
public class Plane: Item
{
public float speed;
public override void Use()
{
Debug.Log($"I'm a Plane, my name is {name}, my speed is {speed}.");
}
}
public class Database: ScriptableObject
{
[SerializeField] private List<Item> items;
public T GetItem<T>(int i) { return (T) items[i]; }
public Item AddItem() { ... }
public Item RemoveItem() { ... }
}
public class DatabaseHolder: MonoBehaviour
{
public Database database;
void Start()
{
Ship ship = database.GetItem<Ship>(0);
// Or...
Plane plane = database.GetItem<Plane>(1);
}
}

Exposing methods of a class only to a Manager class?

Let's say I have a Screen class. This class has few abstract methods like EnterScreen, ExitScreen that are protected.
I want to change screen only through a ScreenManager class but I cannot since both methods are protected.
If I make methods public, I can make a call to both methods in ScreenManager but then I expose them to other classes that accept Screen class as a parameter thus they can easily call Enter and Exit Screen.
Any idea of how can make a call only through ScreenManager and without exposing both methods to other classes? (Only ScreenManager can change screens)
Thanks in advance.
EDIT: #derHugo provided an answer that I should use namespaces and internal keyword, however, I've already tried this but it's not working as expected (methods of the internal class are still accessible in the namespace that is not the same as the Screen class). I'll provide code below and behavior I'm getting.
namespace Test
{
public abstract class Screen<T>
{
internal abstract void EnterScreen();
internal abstract void ExitScreen();
}
}
// Seperate class
namespace Test
{
public class SimpleScreen : Screen<UnityEngine.GameObject>
{
internal override void EnterScreen() { }
internal override void ExitScreen() { }
}
}
// This DOESN'T HAVE A NAMESPACE but I can STILL access the internal methods of the Screen class.
public class GameManager : MonoBehaviour, IInitializable<SimpleScreen>
{
public void Initialize(SimpleScreen screen)
{
// I CAN REFERENCE .Enter & ExitScreen methods here
}
}
One way to approach it is to inject your screenManager instance into your Screen and pass it these Actions that it has privileged access to:
public abstract class Screen<T>
{
protected abstract Action GetEnterScreenAction();
protected abstract Action GetExitScreenAction();
public Screen(ScreenManagerInterface screenManager)
{
screenManager.SetScreenActions(GetEnterScreenAction(), GetExitScreenAction());
}
}
public class SimpleScreen : Screen<UnityEngine.GameObject>
{
private void EnterScreen() { }
private void ExitScreen() { }
protected override Action GetEnterScreenAction() { return EnterScreen;}
protected override Action GetExitScreenAction() { return ExitScreen;}
public SimpleScreen(ScreenManagerInterface screenManager, ....) : base(screenManager) { }
}
public interface ScreenManagerInterface
{
void SetScreenActions(Action enterScreenAction, Action exitScreenAction);
}
A need to make some public methods available only to some other classes may indicate a design flaw in classes relationship. Maybe moving part of the responsibilities into ScreenManager itself could solve this problem.
As you declare Screen class abstract you intent on public or protected methods that can be overridden. So the only option for you to hide them from other classes is to make them protected.
And the only way to access protected members is from the class itself, derived or nested type.
You can make the class ScreenManger nested inside your Screen and it will default to private or make it public. If I understood your question correctly I made an example but without Unity.
I hope it can help
public interface IInitializable<T>
{
}
public class SimpleScreen : Screen<object>
{
protected override void EnterScreen() { }
protected override void ExitScreen() { }
public class ScreenManager
{
private SimpleScreen _simpleScreen;
public void Awake()
{
_simpleScreen.EnterScreen();
}
}
}
public abstract class Screen<T>
{
protected abstract void EnterScreen();
protected abstract void ExitScreen();
}
public class GameManager : IInitializable<SimpleScreen>
{
public void Initialize(SimpleScreen screen)
{
var screenManager = new SimpleScreen.ScreenManager();
screenManager.Awake();
screen.
}
}

Force a child class to initialise a parent property after computation

I have a child class Bicycle that inherits from Agent. The agent has a property which depends on the bicycle to define it. Namely, the physics model for the agent needs to be initialised with the velocity and acceleration constraints which are defined on a per-bicycle-basis and would be different for another type of agent.
The problem I have is that I cannot pass the parameters I need to calculate (the velocity/acceleration require calculations to draw them from a theoretical distribution) for this in the base() constructor because of course the child class hasn't yet been instantiated.
The calculations are done once per bicycle instance but are used multiple times so a simple static method won't do the job. I can just call a protected method in the parent after they're calculated but AFAIK there's no way to enforce this in the child, or more particularly in any future children which I might not write.
So for example, I could:
public abstract class Agent
{
protected IPhysics PluginPhysics { get; set; }
protected Agent(...)
{
}
}
public class Bicycle : Agent
{
private double maxA;
public Bicycle(Object anotherParameter) : base(...)
{
maxA = ComputationOfMaxA();
this.PluginPhysics = new Physics(anotherParameter, maxA);
}
private static double ComputationOfMaxA()
{
...
}
...
}
or I could:
public abstract class Agent
{
protected IPhysics PluginPhysics { get; private set; }
protected Agent(...)
{
}
protected void SetupPhysics(Physics physics)
{
this.PluginPhysics = physics;
}
}
public class Bicycle : Agent
{
private double maxA;
public Bicycle(Object anotherParameter) : base(...)
{
maxA = ComputationOfMaxA();
SetupPhysics(new Physics(anotherParameter,maxA));
}
private static double ComputationOfMaxA()
{
...
}
...
}
I'd rather not do either of those as there's no compile-time way to ensure that the child initialises PluginPhysics that I can think of, and I'd rather PluginPhysics not be able to be changed once it's been initialised. I'd also rather not have the parts of the parameters that need to go into Physicsoutside the Bicycle class. I appreciate that all these things might not be simultaneously possible.
So short of strongly worded documentation or a bunch of run-time null checks in the parent class before any of the relevant class objects are called on, is there an obvious C#-ish way I'm missing of forcing a child to initialise a parent class field before use if you can't do it in the constructor?
d4Rk's answer was very close, however you should try not call virtual methods from a constructor as bad things can happen. However if you use a combination of Lazy loading tricks and ISupportInitialize you can defer the creation of the plugin till after the constructor is finished.
public abstract class Agent : ISupportInitialize
{
private bool _initialized = false;
private IPhysics _pluginPhysics;
protected IPhysics PluginPhysics
{
get
{
if(!_initialized)
EndInit();
return _pluginPhysics;
}
}
protected Agent(...)
{
}
protected abstract IPhysics CreatePhysics();
ISupportInitialize.BeginInit()
{
//We make this a explicit implementation because it will not
//do anything so we don't need to expose it.
}
public void EndInit()
{
if(_initialized)
return;
_initialized = true;
_pluginPhysics = CreatePhysics();
}
}
public class Bicycle : Agent
{
private double maxA;
Object _anotherParameter;
public Bicycle(Object anotherParameter)
{
_anotherParameter = anotherParameter;
}
protected override IPhysics CreatePhysics()
{
ComputationOfMaxA();
return new Physics(anotherParameter, maxA);
}
}
The user of your class will need to call EndInit() after they get a object back to cause the IPhysics object to be created, however if they forget to call the initialize function the getter on the physics object will trigger the initialize call itself the first time it is used.
You could do everything I have shown without the ISupportInitialize interface and just having a public Initalize() method on the base class but I like to expose framework interfaces when they fit.
What about enforcing the subclass to implement a CreatePhysics method, and call this in the base ctor?
Like this:
public abstract class Agent
{
protected IPhysics PluginPhysics { get; private set; }
protected Agent(...)
{
var physics = CreatePhysics();
SetupPhysics(physics);
}
void SetupPhysics(IPhysics physics)
{
this.PluginPhysics = physics;
}
protected abstract IPhysics CreatePhysics();
}
public class Bicycle : Agent
{
private double maxA;
protected override IPhysics CreatePhysics()
{
ComputationOfMaxA();
return new Physics(maxA);
}
}
How about making the constructor for Agent take the IPhysics object and making it protected Then in your Bicycle class, you are forced to call the constructor on the base which sets up your class property:
public class Agent
{
protected IPhysics PluginPhysics { get; private set; }
protected Agent(IPhysics physicsPlugin)
{
PluginPhysics = physicsPlugin;
}
}
public class Bicycle : Agent
{
public Bicycle(IPhysics physicsPlugin)
: base(physicsPlugin)
{
Console.WriteLine("Bicycle ctor");
}
}

Instantiating classes with identical properties and methods

I have 2 classes that manage the operation of 2 different brands of net cam, each have exactly the same public members, but very different private members. One or the other of them is docked inside of a Pane class ( which type is decided at run time). The Pane class provides additional display functionality plus control of whichever camera is docked. When an application instantiates the Pane class, and specifies the type of camera is to be docked, I would like to have the Pane instantiate the proper camera class so that it can call on that class.
My issue is illustrated in the last line of this example code... camera doesn't have a .Start() method because camera is of type Object not of one of the two CameraType types.
How do I get 'object camera' to expose at design time the members of the assigned class so that Pane.Start() winds up calling the Start() method of the class assigned in the switch/case block?
Thank you for your time,
Dave
public class CameraType1 //not to be used directly
{
public CameraType1()
{
Stuff specific to this type of camera
}
public void Start()
{
// Stuff specific to starting a stream to this type
}
}
public class CameraType2 //not to be used directly
{
public CameraType2()
{
// Stuff specific to this type of camera
}
public void Start()
{
// Stuff specific to starting a stream to this type
}
}
public class Pane
{
object camera;
public Pane(string CameraTypeToDeploy)
{
switch (CameraTypeToDeploy)
{
case "Type1":
camera = new CameraType1();
break;
case "Type2":
camera = new CameraType2();
break;
}
}
public void Start()
{
camera.Start(); //wrong... camera doesn't have a Start() method
}
}
You need a common base type. You can define an interface that all camera types implement or you can create an abstract base type from which all cameras inherit. Or you can make both. Program against the interface and supply a base class implementing the interface and providing implementations of members commonly used.
public interface ICamera
{
string Name { get; }
void Start();
}
public abstract class CameraBase : ICamera
{
public abstract void Start(); // Needs to be overridden in non abstract classes.
public virtual string Name { get; protected set; } // May be overridden.
}
public class CameraType1 : CameraBase
{
public CameraType1()
{
// Stuff specific to this type of camera
Name = "Type 1";
}
public override void Start()
{
// Stuff specific to starting a stream to this type
}
}
public class CameraType2 : CameraBase
{
public CameraType2()
{
// Stuff specific to this type of camera
Name = "Type 2";
}
public override void Start()
{
// Stuff specific to starting a stream to this type
}
}
public class Pane
{
ICamera camera;
public Pane(string CameraTypeToDeploy)
{
switch (CameraTypeToDeploy) {
case "Type1":
camera = new CameraType1();
break;
case "Type2":
camera = new CameraType2();
break;
}
}
public void Start()
{
camera.Start(); //OK, all cameras have a Start() method
}
}
An interface gives a great flexibility and a high degree of decoupling; however, it does not provide any implementation that can be reused.
A common base class (abstract or not) without interface creates a high degree of coupling between the classes but can provide ready to use implementations of members for all deriving classes.
You can combine the advantages of both, as shown in my example. If a camera should be very different from all the others, you could still decide to let it implement the interface directly without deriving from CameraBase. Of cause you can also have several camera base classes for different groups of resembling cameras (e.g. different camera types from the same company with similar APIs).
UPDATE #1
According to your comment, your camera classes are derived from vendor supplied base types. You can still let them implement an interface.
public class VendorSpecificCamera
{
public string Name { get; }
public bool VendorSpecificStart(int mode, int framesPerSecond)
}
public class CameraType1 : VendorSpecificCamera, ICamera
{
// The 'Name' property is inherited from the vendor specific base class and
// is therefore already implemented in this example.
public bool CameraStarted { get; private set; }
public void Start()
{
CameraStarted = VendorSpecificStart(2, 25);
}
}
In case the vendor type has already members matching the signature of the interface members, you don't have to do anything in your derived class. If not, just supply the missing members.
UPDATE #2
In case a vendor specific camera class should be sealed, you cannot derive your own class from it. You would then create a wrapper class around the vendor class
public class CameraType1 : ICamera
{
private VendorSpecificCamera _camera;
public CameraType1()
{
_camera = new VendorSpecificCamera();
}
public string Name { get { return _camera.Name; } }
public bool CameraStarted { get; private set; }
public void Start()
{
CameraStarted = _camera.VendorSpecificStart(2, 25);
}
}
You can also make the vendor specific camera visible through a read-only property, allowing you to access vendor specific properties.
public VendorSpecificCamera Camera { get { return _camera; } }
You make both implement an interface ICamera:
public interface ICamera
{
void Start();
}
public class Camera1 : ICamera
{
// your existing implementation
}
public class Camera2 : ICamera
{
// your existing implementation
}
public class Pane
{
ICamera camera;
public Pane(string CameraTypeToDeploy)
{
// Your existing code
}
}
public void Start()
{
camera.Start(); //ok, ICamera has a start method
}
}
Interfaces is the simplest way to tell the program that several classes provides the same functionality and can be used interchangably. The other option (abstract classes or simple inheritance) tends to make everything more confusing, but is sometimes an option as well.
Create an interface which contains the .Start() method and have both CameraType1 and CameraType2 implement it. And have the property camera on Pane be of that interface.
public Interface CameraType
{
void Start();
}
public class CameraType1 : ICameraType //not to be used directly
{
public CameraType1()
{
Stuff specific to this type of camera
}
public void Start()
{
// Stuff specific to starting a stream to this type
}
}
public class CameraType2 : ICameraType //not to be used directly
{
public CameraType2()
{
// Stuff specific to this type of camera
}
public void Start()
{
// Stuff specific to starting a stream to this type
}
}
public class Pane
{
ICameraType camera;
public Pane(string CameraTypeToDeploy)
{
switch (CameraTypeToDeploy)
{
case "Type1":
camera = new CameraType1();
break;
case "Type2":
camera = new CameraType2();
break;
}
}
public void Start()
{
camera.Start();
}
}

Categories