Before I explain my problem, keep in mind that I went for an architecture like this because this is going to be used in an inventory system under Unity, so I had to separate the Item which is a MonoBehavior which is just something in the world from its Data which are just values used for Inventory purposes... If that makes sense.
I have an architecture that goes like this:
public class ItemData
{
// some fields...
public string _name; //should be private with its properties but it doesn't matter in the current example
}
public class EquipmentData : ItemData
{
// some additional fields...
public float _weight;
}
public class Item
{
private ItemData _data;
//Properties
public virtual ItemData Data { get; set; }
}
public class Equipment : Item
{
//Properties
public override ItemData Data
{
get { return _data as EquipmentData; }
set { _data = value as EquipmentData; }
}
}
So, basically I have an item hierarchy that goes even deeper but 1 level is enough to explain myself. (It keeps going like Weapon : Equipment)...
The thing is, if I leave private ItemData _data; in Item class, and add a private EquipmentData _eData; in Equipment class, I will have the ItemData fields twice since EquipmentData inherits ItemData and so on for the other derived classes... getting it a third time if I have a class that derives from Equipment etc...
Something like:
public class Item
{
private ItemData _data;
}
public class Equipment : item
{
private EquipmentData _eData;
}
The fields of ItemData such as _name will appear twice in Equipment and I don't want that...
So I am guessing there is something wrong with my architecture, and there might be a way around this method that looks kind of "dirty", but I couldn't find anything specific for this problem online and I have reached my limits.
What I have tried:
I have tried using the keyword new in Equipment thinking I could hide the initial protected ItemData _data; that is in my Item class, and then have protected new EquipmentData _data; in Equipment but it obviously does not let me since to Shadow _data it needs to be the same type, doesn't seem to work with derived types.
Also, as shown in my code sample, I have tried overriding the property to return the proper type depending on the class it is called in, the casts always return null...
I still find it strange that I ended up trying to implement something like that, so I am open to new ideas to restructure things in a better way, or if someone has a solution I haven't thought of to keep things that way but make them work, it'd be very nice.
I hope I was detailed enough in my problem, if not I can clear things up if needed.
What You need is Generic Classes. By this, You can assign each Item its proper type of ItemData. So Equipment will have its EquipmentData assigned.
//TItemData is a name of this generic type.
//It could be T for example, just like variable name.
//These type names start from T by convention.
//This is not a new class or something like that.
//where TItemData : ItemData is a constraint,
//that assumes that this type should be a subtype of ItemData
public abstract class Item<TItemData> where TItemData : ItemData
{
protected TItemData Data;
}
//EquipmentData is a subtype of ItemData, so it fits here.
//No chances to write, for example, IntEquipment : Item<int> ,
//because int does not derive from ItemData.
public class Equipment : Item<EquipmentData>
{
//here Data will be of EquipmentData type, without any casting.
}
By implementing the above You will achieve Type Safety.
EDIT
To make a class that properly extends Equipment (let's call it a Weapon), and has its proper ItemData (let's call it WeaponData), You need to write something like this:
Edit Equipment, and make it abstract:
public abstract class Equipment<TEquipmentData>
: Item<TEquipmentData>
//this constraint is VERY important, as EquipmentData derives from ItemData, thus fulfill Item<TItemData> constraint.
where TEquipmentData : EquipmentData
{
//Data will have EquipmentData type here.
}
Create WeaponData
public WeaponData : EquipmentData
{
}
Create Weapon
public class Weapon : Equipment<WeaponData>
{
//Data will have WeaponData type here.
}
This works:
public class OverridePropertiesWithSameField
{
public void Test()
{
ChildItem ci = new ChildItem();
ChildItemData cid = new ChildItemData();
cid.ItemDataProp = "ItemDataProperty"; // Inherited
cid.ChildItemDataProp = "ChildItemDataProp"; // Specific
ci.ItemData = cid;
// You know you need ChildItemData type here.
var childItemData = ci.ItemData as ChildItemData;
string itemDataProp = childItemData.ItemDataProp;
string childItemDataProp = childItemData.ChildItemDataProp;
}
}
public class Item
{
protected ItemData data;
public virtual ItemData ItemData { get; set; }
}
public class ChildItem : Item
{
public override ItemData ItemData
{
get { return base.data; }
set { base.data = value; }
}
}
public class ItemData
{
public string ItemDataProp { get; set; }
}
public class ChildItemData : ItemData
{
public string ChildItemDataProp { get; set; }
}
You can use a generic type parameter, with a generic type constraint (where).
public class Item<DATA> where DATA : ItemData
{
public virtual DATA Data { get; set; }
}
Now your class can use a specific ItemData:
Item<ItemData> has property
public virtual ItemData Data { get; set; }
Item<EquipmentData> has property
public virtual EquipmentData Data { get; set; }
Item<ANOTHER> has property
public virtual ANOTHER Data { get; set; }
Related
First of all apologize for long post nevertheless i wanted to highlight problem exactly and to be most readable and understandably. I am developing architecture of my program which will be responsible for files/databases data gather and face some architecture issues so far. All information step by step down below.
Let's consider following code below:
public interface IWatchService<TEntity> where TEntity : IEntity
{
IList<TEntity> MatchingEntries { get; set; }
}
public interface IWatchServiceDatabase<TEntity> : IWatchService<TEntity> where TEntity : IDatabaseEntity
{ }
public interface IWatchServiceFiles<TEntity> : IWatchService<TEntity> where TEntity : IFileEntity
{ }
class Database : IWatchServiceDatabase<DatabaseQuery>
{
public IList<DatabaseQuery> MatchingEntries { get; set; }
}
class Files : IWatchServiceFiles<CsvFile>
{
public IList<CsvFile> MatchingEntries { get; set; }
}
class Consumer
{
public IWatchService<IEntity> WatchService { get; set; }
public Consumer(IWatchService<IEntity> watchService)
{
WatchService = watchService;
var newList = WatchService.MatchingEntries;
}
public void AddNewEntries(IEntity entity) => WatchService.MatchingEntries.Add(entity);
}
class Program
{
static void Main(string[] args)
{
IWatchServiceDatabase<DatabaseQuery> db = new Database();
IWatchServiceFiles<CsvFile> filesCsv = new Files();
var dbConsumer = new Consumer(db); //cannot convert from 'IWatchServiceDatabase<DatabaseQuery>' to 'IWatchService<IEntity>'
var filesCsvConsumer = new Consumer(filesCsv); //cannot convert from 'IWatchServiceFiles<CsvFile>' to 'IWatchService<IEntity>'
dbConsumer.AddNewEntries(new DatabaseQuery());
dbConsumer.AddNewEntries(new CsvFile()); //illegal cause it's not FileConsumer !!!
filesCsvConsumer.AddNewEntries(new CsvFile());
filesCsvConsumer.AddNewEntries(new DatabaseQuery()); //illegal cause it's not DbConsumer !!!
}
}
public interface IEntity { }
public interface IFileEntity : IEntity
{
int Id { get; set; }
string Name { get; set; }
}
public interface IDatabaseEntity : IEntity { }
public class CsvFile : IFileEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class XmlFile : IFileEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DatabaseQuery : IDatabaseEntity { }
We have two errors there:
var dbConsumer = new Consumer(db);
var filesCsvConsumer = new Consumer(filesCsv);
Errors:
cannot convert from 'IWatchServiceDatabase' to 'IWatchService'
cannot convert from 'IWatchServiceFiles' to 'IWatchService'
This seems to be understandable because otherwise "we would be able" to add CsvFile or XmlFile to dbConsumer where generic IDatabaseEntity is expected and CsvFile and XmlFile are in fact IFileEntity and from the other hand DatabaseQuery to filesConsumer which expects IFileEntity and DatabaseQuery is IDatabaseEntity
//Database related
dbConsumer.AddNewEntries(new DatabaseQuery());
dbConsumer.AddNewEntries(new CsvFile()); //illegal cause it's not FileConsumer !!!
//Files related
filesCsvConsumer.AddNewEntries(new CsvFile());
filesCsvConsumer.AddNewEntries(new DatabaseQuery()); //illegal cause it's not DbConsumer !!!
From my understanding this is the clue why compiler raise those errors and which is fine. Therefore I've decided to overcome it in this way:
public interface IWatchService<out TEntity> where TEntity : IEntity
{
IEnumerable<TEntity> MatchingEntries { get; }
}
As can be seen i marked generic parameter out and changed IList to IEnumerable because IEnumerable can be only foreached. Without possibility to modify the list.
Now having this there is no possibility to modify MatchingEntries e.g Add() on therefore we are now not able to add e.g CsvFile (IFileEntity) where IDatabaseEntity is expected and vice versa DatabaseQuery (IDatabaseEntity) where IFileEntity is expected. Fine and understandably.
At the end i have two main questions:
What is the benefit to have this: IEnumerable MatchingEntries { get; } since it's {get;} it cannot be initialized or populated with values therefore i would always get empty list when calling that property. Or i am in wrong? Can somebody explain showing based on my code what can be done with it?
Let's imagine i want to have possibility to Add items to this MatchingEntries list and in Consumer class i want still to be able to pass in ctor either Database or Files related classes based on interfaces. How this can be accomplished? Please also show an example based on current code.
Many thanks for your support and hope someone benefit from it as i saw a lot of confusions related to that topic.
First question:
What is the benefit to have this: IEnumerable<T> MatchingEntries { get; } since it's {get;} it cannot be initialized or populated with values therefore I would always get empty list when calling that property. Or I am in wrong? Can somebody explain showing based on my code what can be done with it?
I am confused by the question. The interface says that a class that implements that interface must have a getter of this name and type. It says nothing at all about the contents of that sequence:
interface IFoo<out T>
{
IEnumerable<T> Bar { get; }
}
Now we can implement that interface however we want:
class TigerFoo : IFoo<Tiger>
{
public IEnumerable<Tiger> Bar
{
get
{
return new List<Tiger>() { new Tiger("Tony"), new Tiger("Terry") };
}
}
}
So why you think the returned sequence must be empty, I do not understand.
Similarly, nothing is stopping you from making a class that implements a setter:
class GiraffeFoo : IFoo<Giraffe>
{
public IEnumerable<Giraffe> Bar { get; set; }
}
…
GiraffeFoo gf = new GiraffeFoo();
List<Giraffe> giraffes = new List<Giraffe>() { new Giraffe("Gerry") };
gf.Bar = giraffes;
Nothing stops you from changing the contents of the list:
class TurtleFoo : IFoo<Turtle>
{
private List<Turtle> turtles = new List<Turtle>();
public IEnumerable<Turtle> Bar => turtles;
public void AddATurtle() => turtles.Add(new Turtle("Tommy"));
}
It is a mystery to me why you think you cannot do any of these things. You want to add a member to the collection? Write a method that adds a member to the collection. You just can't put it in the interface if you wan the interface to be covariant. But the interface tells you what services you must provide, not what services you must not provide! I do not understand why you think that an interface tells you what a class cannot do.
Since T is marked as out, you can now use any of these covariantly:
IFoo<Animal> ia1 = new TigerFoo();
IFoo<Animal> ia2 = new GiraffeFoo();
IFoo<Animal> ia3 = new TurtleFoo();
Of course you don't get to use the methods of the class once it is in an interface, but you never get to use the methods of a class once something is in an interface.
Second question:
Let's imagine I want to have possibility to Add items to this MatchingEntries list and in Consumer class i want still to be able to pass in ctor either Database or Files related classes based on interfaces. How this can be accomplished? Please also show an example based on current code.
Just write code that does that. I don't understand what the question is asking. Please clarify the question.
Let's say I've such code
public class Holded
{
protected internal int holdedID = 0;
}
public class Inventory : Holded
{
public Inventory() { }
public void changeHoldedID()
{
this.holdedID = 100;
}
}
public class Equipment : Holded
{
public Equipment() { }
public void writeHoldedID()
{
Console.WriteLine("Holded ID is: {0}!", this.holdedID);
}
}
public class Cargo : Holded
{
public Cargo() { }
}
If I'd call changeHoldedID and then writeHoldedID, console will still output a string like "Holded ID is: 0!". Now what I want to achieve is to have same base class (Holded) in both of classes. So if I'd change holdedID from Inventory, Equipment's writeHoldedID function would output "Holded ID is: 100!". Thanks and regards!
#Edit: More detailed: I have a game. Each person is a character, that owns Equipment, Inventory and Cargo class. Each class contains about 20 slots for "items". Now the thing is, that if you try to move an item, for ex. from inventory, to equipment, and there's such index of item, then the item is "swapped" - goes holded, and now I may throw such holded item into Equipment, Inventory or Cargo. That's why I'm in need to share such class between Eq/Inv/Cargo.
With this inheritance structure, what you are asking is 99.9% impossible.
When you create an Inventory object, you are also creating a Holded object with its own holdedID member. When you create an Equipment object, you get a new Holded object as well, with no relation to the old one. Thus, changes to one objects member won't affect the other, and you want it this way.
*To be clear, you don't get a seperate Holded object when creating a derived class, but it can be helpful to think of it in the way I described it.
I don't know why you want to do what you are asking, but its a pretty good bet you need to rework your understanding of inheritance, objects, and polymorphsim.
Now, I said this was 99.9% impossible. You can mark the base class member static which shares it among all instances of Holded (and derived classes), making your code possible. However, there is almost no chance you actually want to do this. static should only be used when you understand object-oriented design and the consequences of using it.
Have you considered, instead of "is a" inheritance type relationship a "contains a" relationship?
You could do something along the following lines:
public interface IHolded
{
Bar Foo();
}
public class Holded: IHolded { ... }
And now you have two options in how you want to implent Equipment and Inventory:
Directly exposing holded throgh a readonly property:
public class Inventory
{
public Inventory(IHolded holded) { ... }
public IHolded Holded { get; }
}
Having them implement the IHolded interface and delegating
implementation to holded.
public Equipment
{
private readonly IHolded holded;
public Equipment(IHolded holded) { this.holded = holded; }
public Bar Foo() { return holded.Foo() };
}
This way you are injecting a Holded object when creating Equipment and Inventory instances ensuring a consistent state in both instances.
A Dictionary to store the person and their HoldedId might work
public class Holded
{
protected internal static Dictionary<string, int> _personHoldedIDs;
internal string _person;
public Holded(string person)
{
_person = person;
if (_personHoldedIDs == null)
_personHoldedIDs = new Dictionary<string, int>();
if (!_personHoldedIDs.ContainsKey(_person))
_personHoldedIDs.Add(_person, 0);
}
}
public class Inventory : Holded
{
public Inventory(string person) : base(person) { }
public void changeHoldedID()
{
_personHoldedIDs[_person] = 100;
}
}
public class Equipment : Holded
{
public Equipment(string person) : base(person) { }
public void writeHoldedID()
{
Console.WriteLine("Holded ID is: {0}!", _personHoldedIDs[_person]);
}
}
I am attempting to create an application which interacts with a database.
I program in many languages and I loved how easy it was to use Models in an MVC based application.
So my question is, trying to replicate this functionality, I have 2 classes as follows:
Base Class:
public class BaseModel
{
protected string TableName { get; set; }
public BaseModel()
{
}
public void Save()
{
// Save data in derived class to table stored in TableName
}
}
Derived Class:
public class UserModel : BaseModel
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public UserModel()
{
base.TableName = "user";
}
}
From my main application, i want to be able to do the following:
public class Class1
{
public Class1()
{
UserModel model = new UserModel();
model.Field1 = "Value1";
model.Field2 = "Value2";
model.Save();
}
}
Here is where i have hit a problem. I cannot for the life in me figure out, how i would be able to access the properties in UserModel so they can be saved to the database table specified in in the constructor of UserModel to the BaseModel.
Hope this made sense. :)
This code is not my working code, it is a very simple representation of what i would like to achieve so Fields and properties (validators etc) have been dumbed down for ease of reading.
I think that a more "real" example would actually help understand better, but I think that you should create a virtual (or even abstract) method in the base class to get the data to save in the database:
public class BaseModel {
protected virtual string GetDataToSaveInDB() {
// if it makes sense to have the data as a string...
}
public void Save() {
string data = GetDataToSaveInDB();
// do something with data...
}
}
Then you return the data in the derived classes:
public class UserModel : BaseModel {
protected override string GetDataToSaveInDB() {
return "Field1=" + Field1 + ";Field2=" + Field2;
}
}
This is just to illustrate the concept, if you provide more information it will be easier to provide a real answer.
How do you save the data in your DB? How is the structure to the table?
This is in general solved by utilize abstract functions at the base level and then overriding them with specifics in the derived class. So would would set up a call tree in the base but functions that need to know the details of the derived class would be implemented in the derived class.
public class BaseModel
{
protected string TableName { get; set; }
public BaseModel()
{
}
public abstract void Save();
}
In this case where you have one concrete data representation manifested in a base class it may be better to create a DTO object which your business classes take in a utilize. This is the approch of most of the frameworks like entity.
In C# you have object initializers to initialize fields of objects at creation time without using a constructor.
Now I'm wondering if there is an equivalent to classes which means that you can 'initialize' properties of classes when defining subclasses without actually using an override syntax but simply declaring what the value of a known property is.
Example:
public abstract class Car {
public abstract string Name { get; }
}
// usual approach
public class Mustang : Car {
public overwrite string Name { get { return "Ford Mustang"; } }
}
// my idea of avoiding boilerplate code
public class Mustang : Car { Name = "Ford Mustang" }
Is there a way to accomplish this? If there is none, could T4 templates be of any help?
To make rekire's example clearer, you'd write something like:
public abstract class Car
{
public string Name { get; private set; }
protected Car(string name)
{
this.Name = name;
}
}
public class Mustang : Car
{
public Mustang() : base("Mustang")
{
}
}
EDIT: Another option is to use attributes, where you'd write:
[CarName("Mustang")]
public class Mustang : Car
... having written appropriate reflection code in Car. I would strongly recommend that you don't though. Of course, attributes may be useful in your real context.
You could do this via a construtor, where you need to call the base class constructor.
class car {
Public string Name {public get; protected set}
}
That should basically work too.
I'm trying to set up an inheritance hierarchy similar to the following:
abstract class Vehicle
{
public string Name;
public List<Axle> Axles;
}
class Motorcycle : Vehicle
{
}
class Car : Vehicle
{
}
abstract class Axle
{
public int Length;
public void Turn(int numTurns) { ... }
}
class MotorcycleAxle : Axle
{
public bool WheelAttached;
}
class CarAxle : Axle
{
public bool LeftWheelAttached;
public bool RightWheelAttached;
}
I would like to only store MotorcycleAxle objects in a Motorcycle object's Axles array, and CarAxle objects in a Car object's Axles array. The problem is there is no way to override the array in the subclass to force one or the other. Ideally something like the following would be valid for the Motorcycle class:
class Motorcycle : Vehicle
{
public override List<MotorcycleAxle> Axles;
}
but the types have to match when overriding. How can I support this architecture? Will I just have to do a lot of run-time type checking and casting wherever the Axles member is accessed? I don't like adding run-time type checks because you start to lose the benefits of strong typing and polymorphism. There have to be at least some run-time checks in this scenario since the WheelAttached and Left/RightWheelAttached properties depend on the type, but I would like to minimize them.
Use more generics
abstract class Vehicle<T> where T : Axle
{
public string Name;
public List<T> Axles;
}
class Motorcycle : Vehicle<MotorcycleAxle>
{
}
class Car : Vehicle<CarAxle>
{
}
abstract class Axle
{
public int Length;
public void Turn(int numTurns) { ... }
}
class MotorcycleAxle : Axle
{
public bool WheelAttached;
}
class CarAxle : Axle
{
public bool LeftWheelAttached;
public bool RightWheelAttached;
}
2 options spring to mind. 1 is using generics:
abstract class Vehicle<TAxle> where TAxle : Axle {
public List<TAxle> Axles;
}
The second uses shadowing - and this assumes you have properties:
abstract class Vehicle {
public IList<Axle> Axles { get; set; }
}
class Motorcyle : Vehicle {
public new IList<MotorcycleAxle> Axles { get; set; }
}
class Car : Vehicle {
public new IList<CarAxle> Axles { get; set; }
}
void Main() {
Vehicle v = new Car();
// v.Axles is IList<Axle>
Car c = (Car) v;
// c.Axles is IList<CarAxle>
// ((Vehicle)c).Axles is IList<Axle>
The problem with shadowing is that you have a generic List. Unfortunately, you can't constrain the list to only contain CarAxle. Also, you can't cast a List<Axle> into List<CarAxle> - even though there's an inheritance chain there. You have to cast each object into a new List (though that becomes much easier with LINQ).
I'd go for generics myself.
I asked a similar question and got a better answer, the problem is related to C#'s support for covariance and contravariance. See that discussion for a little more information.