I'm making a card game where I assign random effects to cards, so I need to load the effect's code at runtime with just the class name.
I don't know if my abstract class and child are done properly, and I also don't exactly know how to get the class needed from a path.
I know Resouces.Load won't work but I'll leave it there to convey what I wanna do more easily.
public class GameManager : MonoBehaviour
{
public Effect effect;
...
effect = Resources.Load<Effect>("Card/Effects/" + c.cardActual.effect1);
if (effect.Execution())
{
StartCoroutine(TargetAndCastSpell(c,p));
}
This is the code for my abstract class
public abstract class Effect : MonoBehaviour
{
public string targetType;
public List<int> availableTargets;
public int effectTier;
public PlayerHolder playerTarget;
public CardPhysicalInstance minionTarget;
public PlayerHolder caster;
public void EffectX(PlayerHolder PlayerTarget, CardPhysicalInstance MinionTarget)
{
}
public bool Execution()
{
return false;
}
}
And lastly the child I want to load in runtime
class Spark : Effect
{
string targetType = "any";
//Deal 1 damage to any target
public bool Execution ()
{
bool canTarget = false;
caster = GameManager.singleton.currentPlayer;
availableTargets = SpellHelper.AvailableTargets();
if (targetType == "any") //Placeholder check
{
canTarget = true;
caster.playerState = GameManager.PlayerState.targeting;
}
return canTarget;
}
...
Any help is deeply appreciated, thanks and sorry about my clear lack of understanding of abstract classes.
Based on comments, I think Overriding is the Droid you are looking for. With Polymorphy there is two ways different Implementations can be resolved.
hiding is possibly by default. However, it is also pretty much useless. It is one of those things we thought we need and now everyone adds it to their OOP language. But aside from not using hiding when I wanted to overwrite, I have never had any use for it.
Overriding is the important thing. However, overriding has to be allowed for a function in the base class that first added it.
In Effect:
//Stil work how it does, but now can be overridden
public virtual bool Execution()
{
return false;
}
In Spark:
//We are actually overriding - not just hiding - Effect.Execution() here
public override bool Execution ()
{
bool canTarget = false;
caster = GameManager.singleton.currentPlayer;
availableTargets = SpellHelper.AvailableTargets();
if (targetType == "any") //Placeholder check
{
canTarget = true;
caster.playerState = GameManager.PlayerState.targeting;
}
return canTarget;
}
You can assign a Spark to a Effect variable, call Execution() and Polymorphy will deal with calling the version of Spark.
Add anotehr Effect sub-class? As long as it also overrides Execution() it works the same.
The Effect version could be empty/turned abstract. Or be kept as a default version for all subclasses.
With hiding you would have to cast it back to Spark to get access to it's variant of the Method. Wich is just extra work with no apparent advantage.
Related
I am writing a spawning system for my android game in Unity and for each type of enemy there is a different controller class, which by the way are not MonoBehaviours, these controller classes are controlled by the WaveController which is controlled by the Master enemy controller which is a MonoBehaviour (this doesn't make any difference, it's just easier to control the data flow).
Every controller inherits from the Controller abstract class, but also from the IController interface which takes in 5 generic arguments. You might already guessed it, but the data and functionality is split up since this was the whole spawning process of an enemy is a 5 step pipeline:
The Master controller determines the wave data
The Wave controller determines which controllers (since each enemy type has it's own controller) should be used and how
The controllers determine how and where enemies are spawned
Enemy objects are pulled from an object pool and spawned
Enemies are spawned with specific spawn data which is funneled through a scriptable object that is assigned to the Master controller
Without getting any more deeper into how this system works, my problem is that I have no way of updating a specific controllers data without separately referencing each type of controller data which would get really messy since like I said: the data referencing is inherited from the IController interface which requires 5 generic arguments :P
It's really a waste of my time to write the same code for each of controllers, and later even forget to add new controllers to the function.
PS all generic references are constrained by abstract classes.
I tried referencing each controller data separately, but this was too messy.
I also tried to abstract the ControllerData class, and having each controller have it's own ControllerData class that also inherits from an IControllerData interface for the generic referencing, but this didn't work since one of the 5 generic references also requires 2 generic references, BUT you can't cast nested generic arguments >:(
I just wish C# had wildcards like Java...
public class ObstacleControllerData<ObstacleType, ObstacleScriptableObject, SpawnScriptableObject, SpawnMetricsScriptableObject, SpawnArguments>
where ObstacleType : Obstacle
where ObstacleScriptableObject : ObstacleSO
where SpawnScriptableObject : SpawnSO
where SpawnMetricsScriptableObject : SpawnMetricsSO<ObstacleScriptableObject, SpawnScriptableObject>
where SpawnArguments : SpawnArgs
{
public float LastSpawnTime;
public SpawnMetricsScriptableObject SpawnMetrics { get; private set; }
public readonly ObstaclePool<ObstacleType, ObstacleScriptableObject, SpawnScriptableObject, SpawnArguments> Pool;
public ObstacleControllerData(SpawnMetricsScriptableObject spawn_metrics, ObstacleController controller)
{
LastSpawnTime = Time.time;
SpawnMetrics = spawn_metrics;
Pool = new(spawn_metrics.Data, controller, spawn_metrics.MaxActiveObstacles);
}
public void UpdateData(SpawnMetricsScriptableObject spawn_metrics)
{
Debug.Log("test update data");
}
}
public abstract class ObstacleController
{
public void TrySpawn()
{
if (CanSpawn())
Spawn();
}
protected abstract void Spawn();
protected abstract bool CanSpawn();
}
public class MissileController : ObstacleController, IObstacleController<Missile, MissileSO, MissileSpawnSO, MissileSpawnMetricsSO, MissileSpawnArgs>
{
private ObstacleControllerData<Missile, MissileSO, MissileSpawnSO, MissileSpawnMetricsSO, MissileSpawnArgs> _data;
public ObstacleControllerData<Missile, MissileSO, MissileSpawnSO, MissileSpawnMetricsSO, MissileSpawnArgs> ControllerData { get { return _data; } }
private int _spawnRandomTarget;
private List<int> _randomNumbers;
protected override bool CanSpawn()
{
if (_data.Pool.ActiveObstaclesControlled == _data.SpawnMetrics.MaxActiveObstacles)
return false;
if (_data.LastSpawnTime > 0f && Time.time - _data.LastSpawnTime < _data.SpawnMetrics.Interval)
return false;
if (SpawnChanceSuccessful())
{
_data.LastSpawnTime = Time.time;
return true;
}
else if (Time.time - _data.LastSpawnTime >= _data.SpawnMetrics.Interval)
{
_data.LastSpawnTime = Time.time;
return false;
}
return false;
}
protected override void Spawn()
{
MissileDirection direction = RandomDirection;
MissileSpawnArgs spawn_args = new MissileSpawnArgs(RandomSpawnPosition(direction), direction);
_data.Pool.SpawnObstacle(_data.SpawnMetrics.GetRandomSpawnData(), spawn_args);
}
public MissileController(MissileSpawnMetricsSO spawn_metrics)
{
_randomNumbers = new();
_spawnRandomTarget = Random.Range(0, 100);
_data = new(spawn_metrics, this);
}
}
public interface IObstacleController<Obstacle, ObstacleScriptableObject, SpawnScriptableObject, SpawnMetricsScriptableObject, SpawnArguments>
where Obstacle : JumpMaster.Obstacles.Obstacle
where ObstacleScriptableObject : ObstacleSO
where SpawnScriptableObject : SpawnSO
where SpawnMetricsScriptableObject : SpawnMetricsSO<ObstacleScriptableObject, SpawnScriptableObject>
where SpawnArguments : SpawnArgs
{
public ObstacleControllerData<Obstacle, ObstacleScriptableObject, SpawnScriptableObject, SpawnMetricsScriptableObject, SpawnArguments> ControllerData { get; }
}
I need to call the UpdateData function from a reference of the ObstacleController
The answer was actually really simple, but the array of forums and videos were quite confusing to me for some reason. I just used the dynamic keyword like:
dynamic controller = controllers[0];
controller.ControllerData.UpdateData(data);
To elaborate, the dynamic keyword is used to define an object of unknown type and property. What ever function you call, or property use, no matter if the the name syntax is correct there will be no compile error.
If there is an error in the code it wont be detected by the compiler, rather it will be noticed at runtime which is the only downside to using the dynamic keyword. Use it with care and only when you absolutely need it and know that there will be no error.
Since this is a Unity related question, the dynamic keyword works only if you set your projects .NET compatibility to 4.x
I have the following object (class).
namespace Temp.Models
{
public class CurrentClass
{
private double _firstCoefficient;
private double _secondCoefficient;
public double FirstCoefficient
{
get { return _firstCoefficient; }
set { _firstCoefficient= value; }
}
public double SecondCoefficient
{
get { return _secondCoefficient; }
set { _secondCoefficient= value; }
}
}
}
The following class utilizes the above object and therefore initializes the object as follows:
namespace Temp.Models
{
public class MainClass
{
private CurrentClass _currentClass = new CurrentClass();
public CurrentClass CurrentClass
{
get { return _currentClass; }
set { _currentClass = value; }
}
}
}
At some point if certain conditions are met I would define the variables as follows:
MainClass currentObject = new MainClass();
//if conditions are met
currentObject.CurrentClass.FirstCoefficient = 0;
currentObject.CurrentClass.SecondCoefficient = 5;
But what if the conditions are never met and I never define the above variables. How and/or what is the best way to check if the object was never defined?
I can do the following check:
if(currentObject.CurrentClass.FirstCoefficient != 0 && currentObject.CurrentClass.SecondCoefficent != 0)
But the values can be defined as 0...So I am not sure how to go about this.
Any help is much appreciated!
These are some principles that can be used for solving the problem with description, samples and brief evaluation/opinion.
1. Parametrization through constructors
According to OOP principles, a constructor is method used to initialize an object to a valid state. The concept of immutability takes this even further, disallowing any changes, completely avoiding invalid state.
There is also a possibility of compromise where the API of an object disallows invalid states.
With this concept, you would arrive to:
namespace Temp.Models
{
public class CurrentClass
{
public double FirstCoefficient { get; private set; }
public double SecondCoefficient { get; private set; }
public CurrentClass(double firstCoefficient, double secondCoefficient)
{
FirstCoefficient = firstCoefficient;
SecondCoefficient = secondCoefficient;
}
// if mutability is required - this is needless as the constructor is
// the same but if there was more complex state, methods like this would make
// sense, mutating only parts of the state
public void SetCoefficients(double firstCoefficient, double secondCoefficient)
{
FirstCoefficient = firstCoefficient;
SecondCoefficient = secondCoefficient;
}
}
}
Summary:
Each instantiation of CurrentClass is always in a valid state, avoiding a lot of consistency checks (improved encapsulation)
It takes more code to write (but you save a lot of other code due to the previous point)
You need to know the coefficients beforehand.
2. Using nullable types
Nullable types add the "additional" value to types, the "undefined" state. Reference types (class) are nullable by design while value types (struct) need to be marked nullable, either as Nullable<T> or with the shorthand T?.
This then allows the objects be in invalid state and be specific about it. This goes to the other end of consistency scale from immutability as an object with multiple nullable fields has many invalid states.
Sample code:
namespace Temp.Models
{
public class CurrentClass
{
public double? FirstCoefficient { get; set; }
public double? SecondCoefficient { get; set; }
}
}
Now this gets instantiated quite nicely and can be changed on the fly:
public CurrentClass CreateCurrentClass()
{
var currentClass = new CurrentClass { FirstCoefficient = 1.0 };
var secondCoefficient = RetrieveSecondCoefficient();
currentClass.SecondCoefficient = secondCoefficient;
return currentClass;
}
You'll however need validity checks everywhere the object is used.
public bool IsValid(CurrentClass currentClass)
{
// what if FirstCoefficient has value and SecondCoefficient doesn't,
// is that always an invalid state?
return currentClass.FirstCoefficient.HasValue
&& currentClass.SecondCoefficient.HasValue;
}
Summary:
Very little code is needed to have a DTO up and running
A lot of consistency checks (and related brain pain) are required to work with such model
Encapsulation is lacking - any method taking CurrentClass can alter its validity, therefore making the previous point even worse. This can be eased by usage of read-only interface passed where read-only access is required.
Summing up
There are many other means that usually lay in between the two aforementioned approaches. For example you can use one validity flag (SergeyS's response) per object and ease on the external validity checks but having more code in the class and the need of deeper thinking.
Personally, I prefer immutability. It's more monkey code to write but will definitely pay off down the road thanks to the clean design.
A complex system without immutability is very hard to reason about without extensive knowledge. This is especially painful when working in a team - usually each person only knows a part of the codebase.
The sad thing is that it's not always possible to have evertything immutable (e.g. viewmodels): then I tend to convert objects to an internal immutable model as soon as it's possible.
Given what you already wrote, I would add Initialize() method and Initialized property into your MainClass class. Something similar to this:
public class MainClass
{
private CurrentClass _currentClass = new CurrentClass();
public CurrentClass CurrentClass
{
get { return _currentClass; }
set { _currentClass = value; }
}
public bool Initialized {get; private set;}
public void Initialize()
{
this.CurrentClass.FirstCoefficient = 0;
this.CurrentClass.SecondCoefficient = 5;
this.Initialized = true;
}
}
Call Initialize() method where your conditions met.
Later in code you can just check if(currentObject.Initialized). Notice private setter for `Initialized' property, it will ensure this flag was not accidentally set by external code.
Depending on your needs, you can go further and pass parameters for initialization directly to Initialize() method as parameters.
You have several approaches, like force values to be correct in constructor or have another variable telling if object has no value yet, like System.Drawing.Point has static "Empty" property. But in this case of your simple object your main class is explicitly creating an instance of CurrentClass so at this point this object should be correct and coefficients should be set. If you rely on some other code to set those values to perform some other action later, it is out of scope of these two objects here.
Update: perharps sharing details of what the real problem is would be better, because I have a feeling trying to provide a simpified example ended up in hiding real problem.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
I have been developing in C# for around 12 months now (from scratch, no previous dev experience apart from a little bit of PHP script hacking) and I like to think I have developed my skills to a level which I can write an app and it perform its function perfectly.
however, I am still a little confused about best coding practises, I understand that this code is bad:
class Example1
{
public static Alert GenerateAlert()
{
Alert AlertObject = new Alert();
AlertObject.AlertDatetime = DateTime.Now;
AlertObject.AlertHasRecords = false;
return AlertObject;
}
}
If for example AlertDatetime requires more than a simple line like DateTime.Now; I will end up bulking out a massive function. not good!
However, I cant see a problem with the following two examples (I favour Example 2)
class Example2
{
public static Alert AlertObject = new Alert();
public static Alert GenerateAlert()
{
PopulateAlertDate();
CheckForAlertRecords();
return AlertObject;
}
private static void CheckForAlertRecords()
{
AlertObject.AlertHasRecords = false;
}
private static void PopulateAlertDate()
{
AlertObject.AlertDatetime = DateTime.Now;
}
}
class Example3
{
public static Alert GenerateAlert()
{
Alert AlertObject = new Alert();
AlertObject.AlertDatetime = PopulateAlertDate();
AlertObject.AlertHasRecords = CheckForAlertRecords();
return AlertObject;
}
private static bool CheckForAlertRecords()
{
return false;
}
private static DateTime PopulateAlertDate()
{
return DateTime.Now;
}
}
Is one example better than the other, and if so why? or is there a completely different way of doing it?
Your first example is fine.
If, at a later time, AlertDateTime requires a more complex function to be initialized, you can always refactor your code to something like example 3. Until then, respect the KISS (Keep it simple) and YAGNI principles.
Note that the interface (the publicly available methods and their signature) does not change between examples 1 and 3. This is a good thing. It means that you can move between those styles without having to modify the code that uses your class.
Example 2, however, has a lot of problems:
The information hiding principle basically says that you should not expose something publicly without a good reason. Why would you store your newly generated Alert in a publicly accessible "global variable"?
Example 2 behaves differently: If you call GenerateAlert twice, it will return a reference to the same Alert object both times. (Think about what happens if you call it once today and again tomorrow.)
As a side note, the naming of your methods in Example 3 can be improved. Try to think of each method in isolation: PopulateAlertDate() does not populate the alert date. It returns a date that can be used to populate an alert date. The name GetDefaultAlertDate() might be more appropriate.
+1 for the great answer of Heinzi.
I'll add that in example 3 you are using a variation of the Façade pattern. You are wrapping a class with its complicated & repeated initializing logic, and also hide the interface of this object and expose new methods instead. If later you have several different ways to create the same object, you should consider the Factory pattern.
Pay attention: you should firstly favor placing some of the code in the original class' constructor, if there is no reason of using another variation at a time.
Example 2 resembles the Singleton anti-pattern, which serves another purpose - keeping one instance of a class. This is usually done for services you prefer being created once and for all. Even then, you better look at Dependency Containers for greater unit testing capabilities.
If there's more logic in these functions than just assigning true or false, you might want to use a factory and interfaces. A completely abstracted code following the solid principles would look like:
public class AlertFactory : IAlertFactory {
IAlertDatePopulator alertDatePopulator;
IAlertRecordsChecker alertRecordsChecker;
public AlertFactory(IAlertDatePopulator alertDatePopulator, IAlertRecordsChecker alertRecordsChecker) {
this.alertDatePopulator= alertDatePopulator;
this.alertRecordsChecker = alertRecordsChecker;
}
public Alert GenerateAlert() {
Alert alertObject = new Alert();
alertObject.AlertDatetime = alertDatePopulator.Populate();
alertObject.AlertHasRecords = alertRecordsChecker.Check();
return alertObject;
}
}
with
interface IAlertFactory { Alert GenerateAlert(); }
interface IAlertDatePopulator { DateTime Populate(); }
interface IAlertRecordsChecker { bool Check(); }
You can then add concrete implementations for these interfaces, for example:
public class DateTimeNowAlertDatePopulator : IAlertDatePopulator {
public DateTime Populate() { return DateTime.Now; }
}
public class SomeCalculationAlertDatePopulator : IAlertDatePopulator {
public DateTime Populate() { return /* something calculated */; }
}
resp.
public class AlwaysFalseAlertRecordsChecker : IAlertRecordsChecker {
public bool Check() { return false; }
}
public class SomeCalculationAlertRecordsChecker : IAlertRecordsChecker {
public bool Check() { return /* something calculated */; }
}
Then you can create configured factories:
public class DateNowAndRecordsFalseAlertFactory : AlertFactory {
public DateNowAndRecordsFalseAlertFactory ()
: base (new DateTimeNowAlertDatePopulator(), new AlwaysFalseAlertRecordsChecker()) { }
}
public class DateNowAndCalculatedRecordsAlertFactory : AlertFactory {
public DateNowAndCalculatedRecordsAlertFactory ()
: base (new SomeCalculationAlertDatePopulator(), new AlwaysFalseAlertRecordsChecker()) { }
}
And then just use your factory:
var alertFactory = new DateNowAndRecordsFalseAlertFactory ();
var myAlert1 = alertFactory.GenerateAlert();
var alertFactory2 = new DateNowAndCalculatedRecordsAlertFactory();
var myAlert2 = alertFactory2.GenerateAlert();
etc. This seems a lot of code for a simple functionality, but if you expect a lot of extensions with lots of logic coming up, then this is clean code following the open/close principle (to be open for extensions (by just adding new interface implementations) but closed for modifications (not needing to modify existing code anymore)).
Most effective when used with dependency injection. You'd then configure your factory like this:
public class DateNowAndRecordsFalseAlertFactory : AlertFactory {
public DateNowAndRecordsFalseAlertFactory (DateTimeNowAlertDatePopulator alertDatePopulator, AlwaysFalseAlertRecordsChecker alertRecordsChecker)
: base (alertDatePopulator, alertRecordsChecker) { }
}
And just do:
var alertFactory = someDiContainer.Resolve<DateNowAndRecordsFalseAlertFactory>();
You are trying to instantiate an object and I don't see a point of having static method for that (there is an answer already with factory, do you really need that?)
In place where you have to create this object simply do
var alert = new Alert();
If you want to customize some of properties after object is created with default values, then here is shortcut
var anotherAlert = new Alert() { AlertDatetime = DateTime.Now };
Normally you should create instance of object in the way usable at most, so if you always have to construct it with current date, this is what constructor normally does:
public class Alert
{
// do not add class name to property
public DateTime DateTime {get; set;}
// this don't need initialization if default value is false
public bool HasRecords {get; set;}
public Alert()
{
DateTime = DateTime.Now;
}
}
I hava a base type (A) which has two derivatives (B and C). The base type is not abstract. So, I have three objects.
The only difference between B and C is that they both have one extra different property:
B.Foo
C.Bar
Now I have conditions like this in my code:
if(myObject is B)
myDatabindB.DataSource = ((B)myReport).Foo);
else if(myObject is C)
myDatabindC.DataSource = ((C)myReport).Bar);
and in another method:
pnlSomePanel.Visible = myObject is B;
pnlSomeOtherPanel.Visible = myObject is C;
But you can imagine that when there's a new type I have to update all my if-else statements. This violates a lot of OO principles.
But the problem is that I can't think of a nice and clean solution to solve this issue.
Do you have a suggestion / idea to solve this problem?
EDIT:
If it matters, I am using the MVP pattern.
First, it's good that you asked this with only three items--it makes fixing problems much faster :). Your code's very generic, so I can only offer generic solutions.
The big goal here is to increase the encapsulation of classes A, B, and C--to make sure that anything relevant to A, B, or C is stored within those classes and not moved to, say, if-statements elsewhere.
We can move the logic for figuring out what the correct datasource is from the Controller (which is doing your binding) to your report. This method's name should be descriptive, like GetReportSubjectLine().
class A{
<snip>
public virtual SomeDataType getDataSourceForViewType(){
throw new NotImplementedException()
}
}
class B{
<snip>
public override SomeDataType getDataSourceForViewType(){
return this.Foo;
}
}
class C{
public override SomeDataType getDataSourceForViewType(){
return this.Bar;
}
}
This code will be reusable if you ever want to make different UI's that still need this type of information from your report to generate whatever graphical view you're generating.
There's no good way around the second problem you presented. We could always move the panel visibility into the reports too, but that increases coupling--how much one class is tied to another--way too much. Your reports should not be tied to a specific view.
The best solution is to add another layer of indirection--in this case, an intermediary class to handle the logic of what panels to make visible when. This way your controller doesn't have to bear the responsibility of managing panel visibilities itself.
public class PanelVisibilityManager{
ICollection<Panel> ManagedPanels {get; set;}
//
public IDictionary<System.Type, ICollection<Panel>> Switchboard {get; set;}
public void TogglePanelsFor(System.Type item){
foreach(var panel in ManagedPanels){
panel.Visible=false;
}
foreach(var panel in Switchboard[item]){
panel.Visible=true;
}
}
Hope this helps!
Strategy Pattern fits here pretty well for the first case
For the second case if you have one to one mapping of your panels you can end up with a static readonly Dictionary<Type, Panel> panels if there are many of types.
There is a tab control in WinForms to show some particular tab as well
One the ways to avoid that type of code is move decisional responability into object itself. For example:
Define somewher collection of A.
List<A> objects = new List<A>{new B(), new C()}
Instead of having if/else use foreach over collection and calll on every object a virtual method defined in A and overriden in childs, like
virtual bool ThisIsMe(A objectToCheck){}
B and C override this method by checking if objectToCheck is their type and return true or false in regard of it.
EDIT
Example:
public class A
{
public virtual bool ThisIsMe(A objectToCheck){}
public virtual object GetData{}
}
public class B : A
{
public override bool ThisIsMe(A objectToCheck)
{
return objectToCheck is B;
}
public override object GetData()
{
return this.Foo;
}
}
public class C : A
{
public override bool ThisIsMe(A objectToCheck)
{
return objectToCheck is B;
}
public override object GetData()
{
return this.Bar;
}
}
Now instead of that if/else, something like this:
foreach(A objA in objects)
{
if(objA.ThisIsMe(myObject))
{
myDatabindB.DataSource = objA.GetData();
break;
}
}
May be also substitude this with some fancy LINQ instruction.
Hope this helps.
How about a Dictionary<Type, Action>?
Then you could do something like this:
var myActors = new Dictionary<Type, Action<BaseClass>>();
myActors.Add(typeof(classA), DoSomethingWithA);
myActors.Add(typeof(classB), DoSomethingWithB);
...
Action actor;
if(myActors.TryGetValue(specialRetrievedOnlyAsBase.GetType(), actor))
{
ResetEverything();
actor(specialRetrievedOnlyAsBase);
}
else
{
// ToDo: What should happen if this type is not supported?
}
...
private void DoSomethingWithA(BaseClass)
{
var classAObject = (ClassA)BaseClass;
// ToDo: What should happen if classA arrives?
}
private void DoSomethingWithA(BaseClass)
{
var classAObject = (ClassB)BaseClass;
// ToDo: What should happen if classB arrives?
}
I have the following code:
public class Parent
{
public string MyField { get; set; }
}
public class Child : Parent
{
protected new int MyField { get; set; }
}
I try and access this with:
static void Main(string[] args)
{
Child child = new Child();
child.MyField = "something";
}
Visual studio 2008 compiles this without comment, but under Mono (2.4.2, Ubuntu) I get the error message
'HideTest.Child.MyField' is inaccessible due to its protection level (CS0122)
Is one implementation or the other more compliant with the standard here?
Edit: Thanks to all the people who have pointed out the bad design. Unfortunately it's a third-party library and changing it significantly isn't practical.
From ECMA-334 (the C# spec) §10.7.1.2 :
A declaration of a new member hides an inherited member only within the scope of the new member.
You can see this behavior by running this test on Microsoft's implementation.
using System;
using NUnit.Framework;
namespace ScratchPad
{
[TestFixture]
public class Class1
{
[Test]
public void InheritanceHiding()
{
var b = new Base();
var d = new Derived();
var baseSomeProperty = b.SomeProperty;
var derivedSomeProperty = d.SomeProperty;
b.GetSomeProperty();
d.GetSomeProperty();
}
}
public class Base
{
public string SomeProperty
{
get
{
Console.WriteLine("Getting Base.SomeProperty");
return "Base.SomeProperty";
}
}
public string GetSomeProperty()
{
return SomeProperty;
}
}
public class Derived : Base
{
protected new int SomeProperty
{
get
{
Console.WriteLine("Getting Derived.SomeProperty");
return 3; //Determined by random roll of the dice.
}
}
public new int GetSomeProperty()
{
return SomeProperty;
}
}
}
Which will output:
Getting Base.SomeProperty //(No Controversy)
Getting Base.SomeProperty //(Because you're calling from public scope and the new member is in protected scope, there is no hiding)
Getting Base.SomeProperty //(No Controversy)
Getting Derived.SomeProperty //(Now because you're calling from protected scope, you get the protected member).
So the property you're accessing from your Main() should be the base class property (as it is in MS.NET), not the derived property (as in Mono), because the new derived member only hides the 'old' base member in protected scope.
Mono is doing something wrong here according to the spec.
Jason's answer is correct but he asks for a justification of this behaviour. (Namely that a hiding method is only hiding within the scope of the hiding method.)
There are a number of possible justifications. One in particular is that this is yet another way in which the design of C# mitigates the Brittle Base Class problem.
FooCorp makes Foo.DLL:
public class Foo
{
public object Blah() { ... }
}
BarCorp makes Bar.DLL:
public class Bar : Foo
{
// stuff not having to do with Blah
}
ABCCorp makes ABC.EXE:
public class ABC
{
static void Main()
{
Console.WriteLine((new Bar()).Blah());
}
}
Now BarCorp says "You know, in our internal code we can guarantee that Blah only ever returns string thanks to our knowledge of our derived implementation. Let's take advantage of that fact in our internal code."
public class Bar : Foo
{
internal new string Blah()
{
object r = base.Blah();
Debug.Assert(r is string);
return (string)r;
}
}
ABCCorp picks up a new version of Bar.DLL which has a bunch of bug fixes that are blocking them. Should their build break because they have a call to Blah, an internal method on Bar? Of course not. That would be terrible. This change is a private implementation detail that should be invisible outside of Bar.DLL.
In general, the .NET implementation of C# should probably be considered "canon". From the documentation on the new Modifier:
A constant, field, property, or type introduced in a class or struct hides all base class members with the same name.
... it seems like the Mono implementation is more correct given this definition. It should be hiding the implementation of MyField in the Parent class, and therefore it should only be accessible with the int MyField signature from the Child class.
Prelude: This code is crazy. If you actually have code in your app like this, fix it now. Either make them both protected or both public!
Regarding the error: The CLR has a lot of really strange 'edge case' rules in it for dealing with things like this. The best place to look for this kind of stuff is usually Eric Lippert's blog.
In saying that though, it looks like mono is actually doing the more sensible thing here in my opinion.
On second look, the C# one makes more sense once you factor in the 'behind the scenes' stuff.
Properties are not "first class" in MSIL. A property in C# or VB is just compiled down to a get and set method (the compiler also sticks an attribute somewhere for bookkeeping).
int MyField { get; set; } will actually produce MSIL for two methods:
void set_MyField(int value);
int get_MyField();
Now, given that your new method has a different type, you'll end up with the following 2 setter methods.
void set_MyField(int value);
void set_MyField(string value);
When you call x.MyField = "string" you're just calling one of those methods. This then boils down to a normal method overloading scenario. It's perfectly valid to have two methods with the same name that take different parameters, so the compiler will just select the string one and carry on it's merry way.
So yeah. The C# one makes sense if you know how the internals work, the Mono one makes more sense if you don't.
Which one is "more correct"? Ask Eric Lippert :-)
Just adding my 2 cents) That's a Mono bug, here is the description.
IMHO the difference is that MS.NET recognize the type string for MyField and sets the value of Parent property and in Mono in just tries to access MyField in Child class.
You are making something that's available through the base class unavailable through the child. You can try that, but it won't actually do anything. People can always just do this:
Parent child = new Child();
and call the method. So if you want the field to be hidden, declare a new one and keep the inherited one public.