Inheriting from generic class that also inherits from a generic singleton - c#

I'm having difficulty inheriting from a generic class that is itself inherited from a generic singleton.
I am trying to make an inventory base class that is a singleton and derive different inventory types from this with different derived items.
Code has been simplified for brevity.
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static bool m_ShuttingDown = false;
private static object m_Lock = new object();
private static T m_Instance;
public static T Instance
{
get
{
if (m_ShuttingDown)
return null;
lock (m_Lock)
{
if (m_Instance == null)
{
m_Instance = (T)FindObjectOfType(typeof(T));
if (m_Instance == null)
{
var singletonObject = new GameObject();
m_Instance = singletonObject.AddComponent<T>();
singletonObject.name = typeof(T).ToString() + " (Singleton)";
DontDestroyOnLoad(singletonObject);
}
}
return m_Instance;
}
}
}
private void OnApplicationQuit()
{
m_ShuttingDown = true;
}
private void OnDestroy()
{
m_ShuttingDown = true;
}
}
public class Inventory<T> : Singleton<Inventory<T>> where T : Item
{
...
}
public class EquipmentInventory : Inventory<Equipment>
{
...
}
public class Item : ScriptableObject
{
public string Name = "Item";
}
public class Equipment : Item
{
public string Name = "Equipment";
}
I can't access the Instance;
private EquipmentInventory equipmentInventory;
private Inventory<Item> inventory;
public void Run()
{
var cachedInventory = Inventory<Item>.Instance; //returns null
var cachedEquipmentInventory = EquipmentInventory.Instance as EquipmentInventory; //returns null
}
Both statements return null.
The purpose of this, is that each inventory type will be a singleton and each type of inventory will be implent different item types, so that the base inventory will use the Item type, while the Equipment inventory will be implemented using the Equipment item type.
Here is an alternate method, which seems to solve this
public abstract class Inventory<T, TClass>
: Singleton<TClass> where TClass
: MonoBehaviour where T : Item
{
}
public class EquipmentInventory : Inventory<Equipment, EquipmentInventory>
{
}
I haven't fully tested this yet with actual code, but will update when I have tested it more thoroughly
Please assist.

The main issue is caused by
public class Inventory<T> : Singleton<Inventory<T>> : where T : Item { }
Here you "pass in" the generic type Iventory<T> into the Singleton even though later you explicitly inherit from that Inventory<T> class.
Here is one possible solution though it might seem like a bit strange workaround at first:
Make your Inventory take a second generic type, use the first one inside the class as needed and "forward" the second one to the Singleton<T> like e.g.
// Note how for the limitation via where you still can use the generic type
// which makes sure no other MonoBehaviour can be passed to TSelf by accident
public class Inventory<TItem, TSelf> : Singleton<TSelf> where TItem : Item where TSelf : Inventory<TItem,TSelf>
{
public TItem reference;
private void Awake()
{
if (!reference) reference = ScriptableObject.CreateInstance<TItem>();
}
}
Then in the implementation your pass additionally in your own final (non-generic) type so it can be properly "forwarded" to the Singleton<T> like e.g.
public class EquipmentInventory : Inventory<Equipment, EquipmentInventory> { }
Note that anyway this class has to be in a separated file called EquipmentInventory.cs otherwise it won't work as component in Unity.
This works now since now you explicitly pass in the type EquipmentInventory for TSelf which is then forwarded to the Signleton<T> so the return type of Instance is explicitly EquipmentInventory.
In general get used to rather have one script file for each individual class/type.
Additionally I would slightly alter your fields in Item and Equipment like e.g.
[CreateAssetMenu]
public class Item : ScriptableObject
{
[SerializeField] private string _name;
public string Name => _name;
private void Awake()
{
_name = GetName();
}
protected virtual string GetName()
{
return nameof(Item);
}
}
and
[CreateAssetMenu]
public class Equipment : Item
{
protected override string GetName()
{
return nameof(Equipment);
}
}
And this is how it looks like e.g.
public class Example : MonoBehaviour
{
public EquipmentInventory equipmentInventory;
[ContextMenu("Run")]
public void Run()
{
equipmentInventory = EquipmentInventory.Instance;
}
}

Related

How can C# cast a generic of type T to T?

In the book "Game development patterns with Unity", there is this piece of code
public class Singleton<T> : MonoBehaviour where T : Component
{
private static T _instance;
public static T Instance => _instance;
public virtual void Awake()
{
if (_instance == null)
{
_instance = this as T;
}
else
{
Destroy(gameObject);
}
}
}
How can C# cast an instance of Singleton<T> to T?
The Singleton class is not meant to be used on its own. You can inherit from Singleton the class you want to create as a singleton pattern like GameManager and use it.
This is example.
public class GameManager : Singleton<GameManager>
{
public void ManagerMethod()
{
// your code.
}
}
public class MyClass
{
void Do()
{
GameManager.Instance.ManagerMethod();
}
}

Implementing multiple behaviors on a child class from a method on a root class

I'm looking to implement a certain behavior but I'm not sure how to implement it.
Given a base class :
public class Base
{
void Start() { }
void Update() { }
}
And these two classes which inherit it.
public class Behavior1 : Base
{
private int member;
void Start() { member = 0; }
void Update() { member++; }
}
public class Behavior2 : Base
{
private string name;
void Start() { name = "some string"; }
void Update() { if(name) { Console.WriteLine(name) } }
}
And then a final class which I wish to inherit the logic of the two sub classes.
public class Child : Base // ? Behavior1, Behavior2
{
void Start() { } // logic and members implemented but don't need to be referenced
void Update() { }
}
How would I go about having the Child class implement the two Behavior classes? I don't think you can inherit more than one class at a time so I can't do that. Is there another construct which can accomplish this?
Wihtout enter to valorate the inheritance, that probably need some think as you can read in the comments, you can do something like this if you want use both behaviors ni a class that doesn't inherith them:
public class Child : Base
{
private readonly Behavior1 _behavior1;
private readonly Behavior2 _behavior2;
public Child()
{
this._behavior1 = new Behavior1();
this._behavior2 = new Behavior2();
}
public override void Start()
{
this._behavior1.Start();
}
public override void Update()
{
this._behavior2.Update();
}
}
You can also inherith from Behavior1 and only add Behavior2 as a field:
public class Child : Behavior1
{
private readonly Behavior2 _behavior2;
public Child()
{
this._behavior2 = new Behavior2();
}
public override void Update()
{
this._behavior2.Update();
}
}
But, as I said, is probably that you find a better solution thinking about your models and their composition/inheritance.

Problem with a Finite State Machine "error CS1503: Argument 1: cannot convert from 'UIManager' to 'GameStateAbstract'"

I'm new in Unity. I have started developing a basic game while learning but the following errors arise when trying to chain the states(it's for doing a menu system).
The errors seems to be the same:
Argument 1: cannot convert from 'UIManager' to 'GameStateAbstract'
Argument 1: cannot convert from 'UIManager' to 'GameStateAbstract' //Happends same file in another line
Argument 1: cannot convert from 'GameManager' to 'GameStateAbstract'
context(UIManager):
public class UIManager : MonoBehaviour
{
#UIManager Singletons
private static UIManager _instance;
public static UIManager Instance
{
get
{
if (_instance == null)
{
Debug.LogError("UIManager is NULL");
}
return _instance;
}
}
#Fields
public GameObject gameOverPanel;
public GameObject onPlayOverlays;
public GameObject startMenuPanel;
public GameStateAbstract _currentState;
public readonly PlayStates onPlayStates = new PlayStates();
public readonly DeadStates onDeadStates = new DeadStates();
public readonly MenuStates onMenuStates= new MenuStates();
public void TransitionToState(GameStateAbstract state)
{
_currentState = state;
_currentState.EnterState(this);
}
public void Context(GameStateAbstract state)
{
_currentState.Conditions(this);
}
void Awake()
{
_instance = this;
TransitionToState(onMenuStates);
}
}
Abstract State:
public abstract class GameStateAbstract
{
public abstract void EnterState(GameStateAbstract layer);
public abstract void Update(GameStateAbstract layer);
public abstract void Conditions(GameStateAbstract layer);
}
Concrete State:
public class MenuStates : GameStateAbstract
{
public override void Update(GameStateAbstract layer)
{
}
public override void EnterState(GameStateAbstract layer)
{
}
public override void Conditions(GameStateAbstract layer)
{
}
}
GameManager:
public class GameManager : MonoBehaviour
{
void Update()
{
UIManager.Instance.CurrentState.Update(this);
}
}
Thanks for answering!
public abstract void Update(GameStateAbstract layer);
Your parameter is a ‘GameStateAbstract’ object but you are passing a ‘GameManager’ object:
UIManager.Instance.CurrentState.Update(this);
(this) in this instance is called from GameManager
Make sure you are passing valid parameters or else you will have compile errors.
Also abstract class should be extended with a child class and the child class should be used to create objects.

Passing a generic <TObject> class to a form

I can't seem to find out the answer to this through searching, so here goes....
I know that I can pass Class objects generically to other classes by utilising this type of code:
public class ClsGeneric<TObject> where TObject : class
{
public TObject GenericType { get; set; }
}
Then constructing in this way:
ClsGeneric<MyType> someName = new ClsGeneric<MyType>()
However, I have an application that requires me to open a Form and somehow pass in the generic type for use in that form. I am trying to be able to re-use this form for many different Class types.
Does anyone know if that's possible and if so how?
I've experimented a bit with the Form constructor, but to no avail.
Many thanks in advance,
Dave
UPDATED: A Clarification on what the outcome I am trying to achieve is
UPDATED: 4th AUG, I've moved on a little further, but I offer a bounty for the solution. Here is what I have now:
interface IFormInterface
{
DialogResult ShowDialog();
}
public class FormInterface<TObject> : SubForm, IFormInterface where TObject : class
{ }
public partial class Form1 : Form
{
private FormController<Parent> _formController;
public Form1()
{
InitializeComponent();
_formController = new FormController<Parent>(this.btnShowSubForm, new DataController<Parent>(new MeContext()));
}
}
public class FormController<TObject> where TObject : class
{
private DataController<TObject> _dataController;
public FormController(Button btn, DataController<TObject> dataController)
{
_dataController = dataController;
btn.Click += new EventHandler(btnClick);
}
private void btnClick(object sender, EventArgs e)
{
showSubForm("Something");
}
public void showSubForm(string className)
{
//I'm still stuck here because I have to tell the interface the Name of the Class "Child", I want to pass <TObject> here.
// Want to pass in the true Class name to FormController from the MainForm only, and from then on, it's generic.
IFormInterface f2 = new FormInterface<Child>();
f2.ShowDialog();
}
}
class MeContext : DbContext
{
public MeContext() : base(#"data source=HAZEL-PC\HAZEL_SQL;initial catalog=MCL;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework") { }
public DbSet<Parent> Child { get; set; }
}
public class DataController<TObject> where TObject : class
{
protected DbContext _context;
public DataController(DbContext context)
{
_context = context;
}
}
public class Parent
{
string Name { get; set; }
bool HasChildren { get; set; }
int Age { get; set; }
}
public class Child
{
string Name { get; set; }
int Age { get; set; }
}
Maybe you've tried this, but you can create a custom class:
public class GenericForm<TObject> : Form where TObject : class
{
// Here you can do whatever you want,
// exactly like the example code in the
// first lines of your question
public TObject GenericType { get; set; }
public GenericForm()
{
// To show that this actually works,
// I'll handle the Paint event, because
// it is executed AFTER the window is shown.
Paint += GenericForm_Paint;
}
private void GenericForm_Paint(object sender, EventArgs e)
{
// Let's print the type of TObject to see if it worked:
MessageBox.Show(typeof(TObject).ToString());
}
}
If you create an instance of it like that:
var form = new GenericForm<string>();
form.Show();
The result is:
Going further, you can create an instance of type TObject from within the GenericForm class, using the Activator class:
GenericType = (TObject)Activator.CreateInstance(typeof(TObject));
In this example, since we know that is a string, we also know that it should throw an exception because string does not have a parameterless constructor. So, let's use the char array (char[]) constructor instead:
GenericType = (TObject)Activator.
CreateInstance(typeof(TObject), new char[] { 'T', 'e', 's', 't' });
MessageBox.Show(GenericType as string);
The result:
Let's do the homework then. The following code should achieve what you want to do.
public class Parent
{
string Name { get; set; }
bool HasChildren { get; set; }
int Age { get; set; }
}
public class Child
{
string Name { get; set; }
int Age { get; set; }
}
public class DataController<TObject> where TObject : class
{
protected DbContext _context;
public DataController(DbContext context)
{
_context = context;
}
}
public class FormController<TObject> where TObject : class
{
private DataController<TObject> _dataController;
public FormController(Button btn, DataController<TObject> dataController)
{
_dataController = dataController;
btn.Click += new EventHandler(btnClick);
}
private void btnClick(object sender, EventArgs e)
{
GenericForm<TObject> form = new GenericForm<TObject>();
form.ShowDialog();
}
}
public class GenericForm<TObject> : Form where TObject : class
{
public TObject GenericType { get; set; }
public GenericForm()
{
Paint += GenericForm_Paint;
}
private void GenericForm_Paint(object sender, EventArgs e)
{
MessageBox.Show(typeof(TObject).ToString());
// If you want to instantiate:
GenericType = (TObject)Activator.CreateInstance(typeof(TObject));
}
}
However, looking to your current example, you have two classes, Parent and Child. If I understand correctly, those are the only possibilities to be the type of TObject.
If that is the case, then the above code will explode if someone pass a string as the type parameter (when the execution reaches Activator.CreateInstance) - with a runtime exception (because string does not have a parameterless constructor):
To protect your code against that, we can inherit an interface in the possible classes. This will result in a compile time exception, which is preferable:
The code is as follows.
// Maybe you should give a better name to this...
public interface IAllowedParamType { }
// Inherit all the possible classes with that
public class Parent : IAllowedParamType
{
string Name { get; set; }
bool HasChildren { get; set; }
int Age { get; set; }
}
public class Child : IAllowedParamType
{
string Name { get; set; }
int Age { get; set; }
}
// Filter the interface on the 'where'
public class DataController<TObject> where TObject : class, IAllowedParamType
{
protected DbContext _context;
public DataController(DbContext context)
{
_context = context;
}
}
public class FormController<TObject> where TObject : class, IAllowedParamType
{
private DataController<TObject> _dataController;
public FormController(Button btn, DataController<TObject> dataController)
{
_dataController = dataController;
btn.Click += new EventHandler(btnClick);
}
private void btnClick(object sender, EventArgs e)
{
GenericForm<TObject> form = new GenericForm<TObject>();
form.ShowDialog();
}
}
public class GenericForm<TObject> : Form where TObject : class, IAllowedParamType
{
public TObject GenericType { get; set; }
public GenericForm()
{
Paint += GenericForm_Paint;
}
private void GenericForm_Paint(object sender, EventArgs e)
{
MessageBox.Show(typeof(TObject).ToString());
// If you want to instantiate:
GenericType = (TObject)Activator.CreateInstance(typeof(TObject));
}
}
UPDATE
As RupertMorrish noted, you can still compile the following code:
public class MyObj : IAllowedParamType
{
public int Id { get; set; }
public MyObj(int id)
{
Id = id;
}
}
And that should still rise an exception, because you just removed the implicit parameterless constructor. Of course, if you know what you are doing, this is hard to happen, however we can forbidden this by using new() on the 'where' type filtering - while also getting rid of the Activator.CreateInstance stuff.
The entire code:
// Maybe you should give a better name to this...
public interface IAllowedParamType { }
// Inherit all the possible classes with that
public class Parent : IAllowedParamType
{
string Name { get; set; }
bool HasChildren { get; set; }
int Age { get; set; }
}
public class Child : IAllowedParamType
{
string Name { get; set; }
int Age { get; set; }
}
// Filter the interface on the 'where'
public class DataController<TObject> where TObject : new(), IAllowedParamType
{
protected DbContext _context;
public DataController(DbContext context)
{
_context = context;
}
}
public class FormController<TObject> where TObject : new(), IAllowedParamType
{
private DataController<TObject> _dataController;
public FormController(Button btn, DataController<TObject> dataController)
{
_dataController = dataController;
btn.Click += new EventHandler(btnClick);
}
private void btnClick(object sender, EventArgs e)
{
GenericForm<TObject> form = new GenericForm<TObject>();
form.ShowDialog();
}
}
public class GenericForm<TObject> : Form where TObject : new(), IAllowedParamType
{
public TObject GenericType { get; set; }
public GenericForm()
{
Paint += GenericForm_Paint;
}
private void GenericForm_Paint(object sender, EventArgs e)
{
MessageBox.Show(typeof(TObject).ToString());
// If you want to instantiate:
GenericType = new TObject();
}
}
I think you can add a new type argument to FormController:
public class FormController<TParent, TChild>
where TParent : class
where TChild : class
{
...
public void showSubForm(string className)
{
IFormInterface f2 = new FormInterface<TChild>();
f2.ShowDialog();
}
}
So as I understand it, you want a Form<T> to open upon some action in the MainForm, with your MainForm using a FormController, as a manager of all your forms, relaying the generic type information to your Form<T>. Furthermore, the instantiated object of your Form<T> class should request an instance of a DatabaseController<T> class from your FormController.
If that is the case, the following attempt might work:
MainForm receives a reference to the FormController instance upon constructor initialization or has another way to interact with the FormController, e.g. a CommonService of which both know, etc.
This allows MainForm to call a generic method of the FormController to create and show a new Form object:
void FormController.CreateForm<T> ()
{
Form<T> form = new Form<T>();
form.Show();
// Set potential Controller states if not stateless
// Register forms, etc.
}
with Form<T> along the lines of:
class Form<T> : Form where T : class
{
DatabaseController<T> _dbController;
Form(FormController formController)
{
_dbController = formController.CreateDatabaseController<T>();
}
}
Now you have a couple of ways for the Form to receive a DatabaseController instance:
1. Your Form<T> receives a reference of the FormController or has another way to communicate with it to call a method along the lines of:
DatabaseController<T> FormController.CreateDatabaseController<T> ()
{
return new DatabaseController<T>();
}
Your FormController does not need to be generic, otherwise you'd need a new FormController instance for every T there is. It just needs to supply a generic method.
Your Form<T> receives an instance of the DatabaseController from the FormController upon constructor initialization:
void FormController.CreateForm ()
{
Form form = new Form(new DatabaseController());
form.Show();
}
with Form<T> being:
class Form<T> : Form where T : class
{
DatabaseController<T> _dbController;
Form(DatabaseController<T> controller)
{
_dbController = controller;
}
}
3. Like with 2 but your Form<T> and DatabaseController<T> provide static FactoryMethods to stay true to the Single Responsibility Priciple. e.g.:
public class Form<T> : Form where T : class
{
private DatabaseController<T> _dbController;
public static Form<T> Create<T>(DatabaseController<T> controller)
{
return new Form<T>(controller);
}
private Form(DatabaseController<T> controller)
{
_dbController = controller;
}
}
4. You can also use an IoC Container to register and receive instances of a specific type at runtime. Every Form<T> receives an instance of the IoC Container at runtime and requests its corresponding DatabaseController<T>. This allows you to better manage the lifetime of your controller and form objects within the application.
Well i'm not gonna go into the details here and will only suffice to some blueprints.
In this scenario i'd use a combination of Unity constructor injection with a generic factory to handle the instantiation given the type in main form.
It's not that intricate, take a look at Unity documentation at
Dependency Injection with Unity
The reason for picking Unity out of all DI containers is it was part of Enterprise Library from Microsoft itself and now continues to live on as an independent library in the form of Nugget. a friend of mine has recently ported Unity to .Net core, too. Simply put, it's hands down the most elaborate container available.
As for the factory i believe it's necessary because you don't wanna create a concrete lookup for handling all possible types, so it clearly has to be a generic factory. I'd advise you to make your factory a singleton and put it in whole another project, thereby separating your UI project from the models and both party will communicate through this DI bridge. you can even take a step further and process your model types using assembly reflection.
sorry for being too general, but i really don't know how familiar are you with these patterns. It really worth taking some time and utilizing these patterns. in my humble opinion there is no escape from these maneuvers if you want a truly scalable software.
You can reach me in private if you're looking for hints on implementation of any of the above-mentioned strategies.
Try Factory method.
public interface IProvider
{
T GetObject<T>();
}
Top-level form:
public class TopLevelForm : Form
{
public TopLevelForm(IProvider provider):base()
{
_provider = provider;
}
private void ShowSecondForm()
{
var f2 = new SecondForm(provider);
f2.Show();
}
}
Second-level form:
public class SecondLevelForm : Form
{
public SecondLevelForm(IProvider provider):base()
{
_data = provider.GetObject<MyEntity>();
}
}
As for IProvider's implementation - there are plenty of methods, starting from the simpliest one, return new T();

C# storing generics state

public abstract class State<T>
{
public virtual Enter(T item)
{
// an empty method
}
}
public class ChaseState : State<FieldPlayer>
{
public override Enter(Player pl)
{
// ...
pl.Fsm.CurrentState = ChaseState.Instance;
//...
}
}
public class TendGoal : State<Goalkeeper>
{
public override Enter(Goalkeeper gk)
{
// ...implementation
gk.Fsm.CurrentState = TendGoal.Instance;
// ...implementation
}
}
public class DefendState : State<Team>
{
public override Enter(Team team)
{
// ....
team.Fsm.CurrentState = DefendState.Instance;
//.....
}
}
"Goalkeeper" and "FieldPlayer" inherit from an abstract class "Player", while "Team" inherits from another class.
public class FSM
{
public /*some type*/ owner; // PROBLEM 1
// OWNER CAN BE TEAM, GOALKEEPEEPER
// OR FIELD PLAYER
public /*some type*/ globalState;
public /*some type*/ currentState;
public /*some type*/ previousState;
public void Update()
{
if (globalState != null)
{
globalState.Execute(owner); // PROBLEM 2
// IF GLOBAL STATE'S TYPE
// IS AN OBJECT, CANT CALL EXECUTE
// OBJECTS TYPE WILL BE KNOWN ONLY
// DURING RUNTIME
}
}
}
Each object of type "Goalkeeper", "FieldPlayer" and "Team" will have a State Machine instance. The problem is.. generics cant be properties.
What should I do ?
If you make State an ordinary interface, not generic, and have its Enter method take another interface that your teams, goalkeeprs, players, etc all implement (it can even just be empty), it ought to work.
public interface IOwner {}
public interface IState
{
void Enter(IOwner item);
}
public class ChaseState : IState
{
public void Enter(IOwner pl)
{
// ...
//...
}
}
public class Player :IOwner { }
public class Something {
IOwner owner = new Team();
IState globalState = new ChaseState();
IState currentState = new DefendState();
public void Update()
{
if (globalState != null)
{
globalState.Enter(owner);
}
else if (currentState != null)
{
currentState.Enter(owner);
}
}
}
After reading your code some more, an Abstract Class is unnecessary here. You should convert State to an interface, ex: IState and remove the generic signature from it. Then your properties in your FSM object can all be public IState globalState, etc..

Categories

Resources