Manage derived classes via a base class when using GetComponent in Unity? - c#

Whenever I try to set up an inheritance hierarchy, I find myself trapped in this scenario over and over.
In the current form, I have a base class that represent all UI elements in my game. I called this class, UI_Toggleable. This class has an enum property that each derived class can set. Here is brief look as to how the code looks like:
public class UI_Toggleable
{
// The Menu Type enum that all derived classes must define.
protected UIMenus menuType;
// Gives any child of this class a toggle function
// to enable/disable UI when needed.
public void ToggleUI()
{
// Toggle Code
}
// Public property Getter - Read Only Access.
// Only derived classes can define the value of the menu type.
public virtual UIMenus MenuType
{
get { return menuType; }
}
}
Now, say a class called InventoryUI derives from Toggleable, we have the following.
public class InventoryUI : UI_Toggleable
{
private void Awake()
{
_instance = this;
menuType = UIMenus.Inventory;
}
public override UIMenus MenuType
{
get { return menuType; }
}
}
Now, if I try to implement a manager for these objects, I will want to get the menu type of each derived class. However, I do not want to ASSUME the type of the UI_Toggleable class. Instead, what I am trying to do is to get any of the derived classes as a UI_Toggleable, and then proceed to call the MenuType method to get its type regardless.
UI_Toggleable toggleable = GetComponent<UI_Toggleable>();
toggleable.MenuType;
The problem with the above is, it would return me the MenuType of the base class instead of the derived class I retrieved as a base class. And that is somewhat expected, but I want to get the MenuType of the derived class WITHOUT doing the following:
if(GetComponent<UI_Toggleable>() is InventoryUI )
InventoryUI toggleable = GetComponent< InventoryUI >();
toggleable.MenuType;
The above works, but it defeats the purpose of me setting up a base class that shares similar properties with children. Doing all these casts and checks just makes the code appear difficult to read and decouple.
Other things I tried include the following:
Create an interface IMenuType that defines a function GetMenuType. Each derived class implements the method, and in my manager, I would do the check if(toggleable is IMenuType). And if true, then attempt to call ((IMenuType)toggleable).GetMenuType.
Let the MenuType property getter be an abstract function that each derived class must implement. But similar to the above cases, I still have to make cast checks before attempting to call the method.
Although not my priority, the MenuType method was not meant to be virtual.

You are not setting menuType of the base class correctly. Rather than setting it in the Awake method of derived classes, set it in the constructor, like this:
public class UI_Toggleable {
public UIMenus MenuType {get;}
// Subclasses must pass the correct menuType here
protected UI_Toggleable(UIMenus menuType) {
MenuType = menuType;
}
}
public class InventoryUI : UI_Toggleable {
// Pass the proper menu type for storing inside the base class
public InventoryUI() : base(UIMenus.Inventory) {
}
}
Note how MenuType is now a read-only property of the base class, rather than a virtual property with overriding.
I cannot really use the constructor. Is it acceptable if I set it in the Awake method instead?
It appears from your code sample that Awake is not being called in time for the base class to supply the correct value. In this case you go with an abstract getter-only property, like this:
public class UI_Toggleable {
public abstract UIMenus MenuType {get;}
}
public class InventoryUI : UI_Toggleable {
public override UIMenus MenuType {
get => UIMenus.Inventory
}
}
Note: Legacy syntax for get => UIMenus.Inventory is get { return UIMenus.Inventory; }

Related

What is the purpose of abstract properties, when we still can use them in our abstract class?

Let's see the example at first:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Person p = new Manager();
Manager p2=new Manager();
p.Name = "Ahmad";
p.Introduce();
p.SayHello();
p2.SayHello();
p2 = (Manager)p;
p2.SayHello();
}
}
public abstract class Person
{
protected Person()
{
Name = "Reza";
Introduce();
}
public abstract string Name { get; set; }
public void SayHello()
{
Name = "Ali";
MessageBox.Show(Name);
}
public abstract void Introduce();
}
public class Manager : Person
{
public new void SayHello()
{
MessageBox.Show("Test2");
}
public override string Name { get; set; }
public override void Introduce()
{
MessageBox.Show("Hello " + Name);
}
}
}
at first i hadn't written constructor for base class.
as far as i know the purpose of abstract method is to force the derived class to implement from it, and because of that we can't implement abstract methods in base class.
then i added an abstract property. and i saw that we can initialize that property in base class and using it.
1st: Wasn't the purpose of abstract to just declare them and let derived class to implement it?
Why can we use the property in base class?
we could just implement a non-abstract property at first, and it would make no difference.
then i added the constructor and things get more complicated. we can use Introduce() method in constructor to call Introduce() from Child class (i understand that from debugger).
so Child inherits from Father here, but we call a method in Child from Father, which is strange and is somehow against the rules of inheritance.
2nd: What have i missed?
Edit:
Wasn't the purpose of abstract to just declare them and let derived
class to implement it? Why can we use the property in base class?
You can't use the abstract property in the base class, as you cannot instantiate it directly. You'd create an abstract property for the same reasons you'd want to create an abstract method, which is make sure your derived types implement it.
Your Name property isn't actually using the base-class instance. When called from SayHello, it will go to your derived-type implementation of Name and use that.
So Child inherits from Father here, but we call a method in Child from
Father, which is strange and is somehow against the rules of
inheritance. What have i missed?
It's not only strange, its an error which might cause a run-time exception, for the fact that the child object hasn't been initialized yet. If you were to access any member of Manager that are instantiated via managers constructor (not via field initialization) inside Introduce, you would get an exception:
public class Manager : Person
{
public Manager()
{
s = "Hello";
}
private string s;
public override string Name { get; set; }
public override void Introduce()
{
Console.WriteLine(s.ToLower());
}
}
When you now call Introduce, you'll see a NullReferenceException, as s hasn't been initialized yet.
If you want to call an abstract method on derived type, make sure you do it after object instantiation. Perhaps via an Initialize method on the base.
You can read Virtual member call in a constructor for more information:
if you make a virtual method call in a constructor, and it is not the
most derived type in its inheritance hierarchy, that it will be called
on a class whose constructor has not been run, and therefore may not
be in a suitable state to have that method called.

Introduce setter for some - but not all - inherited classes

I am having a hard time implementing a property in C# that only has a getter in the abstract base class, but where I need to introduce a setter in one of the derived classes.
Update: For a shorter explanation of a generalized example of this question, see this question. The selected answer has explained why this is currently impossible to do in C#, however, in my mind no satisfactory solution has yet been provided.
An overview of my class diagram is shown below:
My objective is that the two classes TextElementStatic and TextElementReferenceSource should have a Text property with both getters and setters, while the class TextElementReferenceTarget should have a Text property with only a getter. I'm constantly using ITextElement while referencing all of these objects, and I need to ensure that the ITextElement interface only has a getter. Also, the base class TextElement implements a lot of common code, so all classes need to inherit from that class.
My current code looks like this:
Interface: ITextElement
public interface ITextElement
{
string Text { get; }
}
Interface: ITextElementUpdatable
public interface ITextElementUpdatable : ITextElement
{
new string Text { get; set; }
}
Abstract class: TextElement (This is where my problem is, explained below)
public abstract class TextElement : ITextElement
{
// I want to mark this 'abstract', but that causes my problem
public virtual string Text
{
get
{
// NOTE: This should never be called
Debug.Fail("Called virtual Text getter that should never be called");
return default(string);
}
}
}
Abstract class: TextElementUpdatable
public abstract class TextElementUpdatable : TextElement, ITextElementUpdatable
{
// Should have both a getter and a setter
public new virtual string Text { get; set; }
}
Class: TextElementStatic
public class TextElementStatic : TextElementUpdatable
{
// Should have both a getter and a setter
// No Text property declaration
// Inherits Text property from TextElementUpdatable
}
Class: TextElementReferenceSource
public class TextElementReferenceSource : TextElementUpdatable
{
// Should have both a getter and a setter
public override string Text
{
get { return _internalobject.Text; }
set { _internalobject.Text = value; }
}
}
Class: TextElementReferenceTarget
public class TextElementReferenceTarget : TextElement
{
// Should ONLY have a getter
public override string Text
{
get { return _internalobject.Text; }
}
}
So, my issue is: I really want to declare the Text property in the base class TextElement abstract, because it should always be implemented in the derived classes (both TextElementUpdatable, TextElementReferenceSource and TextElementReferenceTarget implements this property). However, if I try to convert the property to public abstract string Text { get; }, then I receive an error in TextElementUpdatable specifying that
TextElementUpdatable.Text hides the inherited property TextElement.Text
Further, if I change the property in TextElementUpdatable from new to override the error message is replaced by:
Cannot override because TextElement.Text does not have an overridable set accessor
Now, I could go back to TextElement and change the property to public virtual string Text { get; private set; } and call it a day, since that method should never be called anyway (which is basically the solution I have now). However, if I or someone create another derived class later on, I want to force me/them to implement the Text-property, hence I would rather mark it abstract than provide a virtual implementation.
Any suggestions on how I can do this the right way - even if it should involve a lot of refactoring?
I know that I could separate the two objectives her, providing one inherited Text property with only a getter, and then introduce a SetText() method in the ITextElementUpdatable interface. However, I'm wondering whether it is possible to find a good solution with properties only.
Another similar question, but without any answers I've been able to use: C# - What should I do when every inherited class needs getter from base class, but setter only for ONE inherited class
It is really an exciting desing problem, but.. You have to use the new keyword what is not a good practice. Try to avoid them.
Of course, property names can be the same in the interfaces, but if both implemented by a class (and one of the props defined without a setter), we have to implement them explicitelly. We have to accept that these properties "conflict".
You could introduce abstract methods:
public abstract class TextElement : ITextElement
{
public string Text { get { return GetText(); } }
protected abstract string GetText();
}
public abstract class TextElementUpdatable : TextElement, ITextElementUpdatable
{
string ITextElementUpdatable.Text
{
get { return GetText(); }
set { SetText(value); }
}
protected abstract void SetText(string text);
}
It can be a bit confusing that you use the same property in your hierarchy with different meanings. Maybe the implementation of ITextElement.get_Text and ITextElementUpdatable.get_Text will diverge later - the interfaces define two independent behavior, and we do not use basic types all the time, like string.
So my suggestion is that you should have a property in ITextElement for read only purpose, and another property in ITextElementUpdatable with different name. In this manner, your methods can be defined as abstract, of course.

Setting a read-only object from a derived class

I am writing a library that other developers in our company will use. A state machine base class has a ReadOnlyCollection<T> of allowed states, etc. Developers need to inherit from this class and set the allowed states.
I want to limit them to initialize the ReadOnlyCollection<T> in the constructor of their derived classes and not be able to modify it later.
If I declare the ReadOnlyCollection<T> as a read-only property in the base class, that does not work since it cannot be modified in the constructor of the derived class.
I imagine this to be a not-so-uncommon scenario. Any elegant way to achieve this short of having developers override the ReadOnlyCollection<T>?
Don't let them initialize by themselves. Make your base class constructor to take the collection as an argument:
public class BaseClass
{
protected readonly Collection someObject;
public BaseClass(Collection object)
{
someObject = object
}
}
So now when the derived class constructor is called it should call base class constructor also with the collection object
otherwise it will be a compile time error. This will make sure that the collection is initialized in the constructor and no where else.
public class Derivedclass : BaseClass
{
public DerivedClass() : base(/*pass the collection object here*/)
{
}
}
Still there is a pit fall in this, if you get the reference of a collection, you can still modify the collection by calling Add or remove method of the collection in the derived class only thing is you cant reinitialize if its readonly.
Does it have to be a ReadOnlyCollection ?
I would go for IEnumerable.
This could be done like this, similar to srsyogesh´s answer:
public abstract class StateMachine
{
public StateMachine(params States[] allowedStates)
{
_allowedStates = allowedStates;
}
private readonly IEnumerable<States> _allowedStates;
public IEnumerable<States> AllowedStates
{
get { return _allowedStates; }
}
}
public class DerivedStateMachine : StateMachine
{
public DerivedStateMachine()
: base(States.State1, States.State2)
{
}
}
Of course, one could still cast the Property back to an array and change it, but that would be kind of criminal. Depends on your audience. To be more bullet proof, you could, instead of just returning the field, iterate over the contents:
public IEnumerable<States> AllowedStates
{
get
{
foreach(var state in _allowedStates)
yield return state;
}
}

Changing propeprty setter behaviour from a base class

I'm working with Telerik RadScheduleView and have implemented the IAppointment class into a child 'Job' class that I have defined. The 'Start' and 'End' (DateTimes) properties are found in the IAppointment class (of which I inherit). Navigating to the 'Public Virtual' method (in the IAppointment class) shows me a { get; set; } but I can't change it to say, for example, display a MessageBox on setting a new value to Start or End. It says MetaData in the tab, is this just something I am not able to edit? Is there a way I can override this access method somehow??
The name IAppointment would indicate to me that it is not a class you are inheriting, but an interface you're implementing - however, I'm not familiar with Telerik products and their naming conventions might just be weird, so, taking what you say at face value, yes you ought to be able to override a property defined as virtual.
If, for example, we have the following class defined somewhere, but accessible, so that we may inherit, and which exposes a virtual member:
public class A
{
public virtual int J { get; set; }
}
Then we can inherit and override - we may still access the base implementation, but also "inject" our own, if required:
public class B : A
{
public override int J
{
get
{
return base.J;
}
set
{
base.J = value;
}
}
}
But I can't for the life of me imagine why you'd want to show a message box from within the logic of property accessors, and can't stress enough that you shouldn't.
As Mr Disappointment mentioned I'd expect IAppointment to be an interface.
If it is indeed a class you could use the new modifier and do something along the lines of this.
public class Job : IAppointment
{
new public DateTime End
{
get
{
//get the value directly from the base class
return base.End;
}
set
{
//display your messagebox here
//then pass the value to the base class
base.End = value;
}
}
}
you can write a property in your derived class which will eventually get and set the propety of base class no need to override.
First of all, your question is confusing. IAppointment isn't a class, its an interface.
Is this your situation?
There is an interface called IAppointment with 2 properties namely Start and End. ( http://www.telerik.com/help/wpf/t_telerik_windows_controls_scheduleview_iappointment.html )
There is a base implementation named AppointmentBase (or the more derived, Appointment). (http://www.telerik.com/help/wpf/t_telerik_windows_controls_scheduleview_appointmentbase.html)
You are building your own class which is inheriting from the AppointmentBase.
You want the setter of AppointmentBase.End to show a messageBox but you dont know how to add this logic.
In that case i have good news.
AppointmentBase only servers virtual members so you can easily override the Set property
class Job : AppointmentBase
{
public override DateTime End
{
get { return base.End; }
set
{
MessageBox.Show("Unbelievable!");
base.End = value;
}
}
}

Accessing subclass in base class... but different

I have a base class that has a subclass (could be a struct i suppose but not sure if it's appropriate) and a method.
class Base
{
protected class SubClass
{
public string word;
public int number;
}
protected void SomeMethod()
{
this.SubClass.word //this is where I'm struggling
}
}
Then i have a couple child classes that implement my baseClass, instantiate the Base.SubClass and add some values to the instantiated class.
class ChildClass1 : Base
{
public childSubClass = new SubClass();
public void DoSomethingRidiculous()
{
childSubClass.word = "WhoFarted";
}
}
class ChildClass2 : Base
{
public childSubClass = new SubClass();
public void DoSomethingRidiculous()
{
childSubClass.word = "ItStinks";
}
}
If possible, I would like to be able to get the value of SubClass.word from within the Base class. I think that my attempt at implementing my idea is probably wrong.
I'm not sure that you really need subclassing / class nesting. Just move out class SubClass declaration and declare protected field/property of SubClass type instead.
public class SubClass
{
public string word;
public int number;
}
public class Base
{
protected SubClass subClassInstance = new SubClass();
protected void SomeMethod()
{
this.subClassInstance.word //this is where I'm struggling
}
}
Then you can access subClassInstance inside both ChildClass1 and ChildClass2
The base class has no field or property of type SubClass, so you definitely cannot do what you propose directly.
One solution would be to add the field
public childSubClass = new SubClass();
to class Base itself. Is there a problem with this?
The other solution would be to use reflection to get the value of the field, assuming that the object you are reflecting on does have such a field. This is really far-fetched and while it might technically allow you to do what you propose, it has a very bad code smell.
I'm not sure why you're making a Sub Class instead of just making those two properties of the base class, but the reason you're having trouble with this line :
this.SubClass.word //this is where I'm struggling
is because you're not instantiating SubClass as a property of the base class.
A base class can not (or should not) access members of derived classes, and usually not even know about derived classes (some exceptions apply, such as in the case of the State Pattern). If the base should have access to a member, it should be declared in the base. If derived classes should also be able to use that member, then mark the member as protected.
class Base
{
protected Foo someFoo;
void Frob()
{
// can access methods/properties of someFoo instance
}
}
class Child
{
public Child()
{
someFoo = new Foo(); // child can also access someFoo
}
}

Categories