How to access the property of List <T> subclasses? - c#

i've a Method which gets a List where T : Item.
How do i access the property from the Subclasses of Item?
private void CreateShopItem<T>(Transform itemTemplate, Transform shopScrollView, List<T> shopItemList)
where T : Item {
shopItemList.Name //this works
shopItemList.power //this is a property from the class cooling and i cant access it
}
Ive 4 subclasses from the base class Item but i can only access the properties from the Class Item
Item Class:
public class Item
{
public int Id;
public string Name;
public double Cost;
public Sprite Sprite;
public bool IsPlaced;
public Vector3 Position;
public Item()
{
Id = 0;
Name = "Default";
Cost = 0;
Sprite = null;
IsPlaced = false;
Position = Vector3.zero;
}
public Item(int id, string name, double cost, Sprite sprite, bool isPlaced, Vector3 position)
{
this.Id = id;
this.Name = name;
this.Cost = cost;
this.Sprite = sprite;
this.IsPlaced = isPlaced;
this.Position = position;
}
}
Sub Class Cooling:
public class Cooling : Item
{
public float power;
public float temp;
public Cooling(int id, string name, double cost, Sprite sprite, bool isPlaced, Vector3 position,
float power, float temp)
{
base.Id = id;
base.Name = name;
base.Cost = cost;
base.Sprite = sprite;
base.IsPlaced = isPlaced;
base.Position = position;
this.power = power;
this.temp = temp;
}
}
What would be a way to access the property of all subclasses from the Base Class?

Normally if a method needs to access a field/property this field would be included in the type that it accepts. While you can cast the item into a derived type, if you have to do it why accept the base type in the first place?
The problem is that if you use ifs or case when you add a new type you need to remember to come back to this piece of code and update it.
BTW. Use properties, not fields. It is the standard way:
public class Item
{
public int Id { get };

What you're asking the code to do doesn't make logical sense. When you use a generic type:
public class Foo<T>
{
public T Value { get; set; }
public void MyMethod()
{
// example code here
}
}
You are saying that you're going to be using a type (T) but you're not really sure yet which type you'll be using. The type will be specified at a later stage. The only assumption made by the compiler is that your T will derive from object.
This is okay, but that also means that you can't actually use this T with any more precision than you can use object. In the above example, you could call this.Value.ToString() because T is definitely an object (and object has the ToString() method), but you cannot call this.Value.Power because T is not known to be of the type Cooling (or a subtype).
You are able to influence what the compiler knows about the specific type that T will be. You've already done so by specifying that T will definitely be some sort of Item (i.e. class or subclass)
public class Foo<T> where T : Item
{
public T Value { get; set; }
public void MyMethod()
{
// example code here
}
}
Because the expectation is now that T is not just an object but also an Item, the compiler allows you to handle your T type with every known property/method of the Item type. You could access things like:
this.Value.Name
this.Value.Cost
this.Value.Sprite
Because these are properties of the Item class which you can definitely expect to see on any subclass of Item, you are allowed to access them when dealing with a T where T : Item generic type parameter.
You're trying to access a specific type's property (Cooling), without having told the compiler that your generic type is definitely going to be a (sub) class of Cooling.
That directly contradicts the core premise of using a generic type, i.e. treating a range of possible types using the same (generic) code. If that generic code were to only work with one specific type (i.e. Cooling), then there'd be no point in trying to make CreateShopItem work for any type that is not Cooling or one of its subtypes.
You need to go back to the drawing board with what it is that you want.
If I can assume that it is correct that CreateShopItem should work for any Item class, then you should inherently be able to write code that is able to handle any Item object without needing to know the specific concrete class being used.
I am very intentionally ignoring upcasting here because it's a bad approach that tries to cover for a bad design. Generic type upcasting is rarely a good idea. It violates OCP (in all but exceedingly rare examples), and would not be a good idea in this particular scenario anyway.

First of all the example that you show for the this one works and this one doesn't should be wrong because the reference that you are using is a List (List for your example) and the both of them will not work unless you get the element that you want to change from that list (with a loop or maybe with LINQ First() etc)
Secondly, If you want to access a field from a subclass while you have a base class object. You need to cast it to that subclass. For example:
class Item{
...
public int Id;
public string Name;
public double Cost;
}
class Cooling : Item {
...
public float power;
}
//Example method to call
private void CreateShopItem<T>(Transform itemTemplate, Transform shopScrollView, List<T> shopItemList)
where T : Item {
var firstItem = shopItemList().First(); // taking the first element just for example
firstItem.Name = "foo"; // this is fine
var coolingItem = (Cooling) firstItem;
coolingItem.power = 1000; // now this is also fine
}
This will fix your current case but I do not recommend to do it because as the subclasses increase this can turn into a big mess of casting disaster between classes.
I recommend to check out boxing/unboxing topic on C# for future use and best practices.

Related

How to sort a List<> that contains derived class objects, by derived class method

I have a double problem here. I need to sort a List<> that I know contains objects of a derived class to the class that the list was declared to contain originally. AND, I need to sort by the return value from a method in that derived class, which takes a parameter. Keep in mind that I already know the List contains objects all of the derived class type.
I've created some sample code here to demonstrate the question since the real code cannot be shared publicly. Note, I have no control over the base conditions here (i.e. the fact that the List<> collection's declared contents are the parent class and that it contains objects of the derived class, which contains a method that takes an argument and returns the values that I need to sort the collection by). So, I doubt I'd be able to use any suggestion that requires changes there. What I think I need is a way to specify (cast?) what is really in the List so I can access the method defined there. But I'm open to other thoughts for sure. Otherwise I'm left with a traditional bubble sort. Thanks.
public class Component
{
public int X;
public int Y;
}
public class ComponentList : List<Component>
{
// Other members that deal with Components, generically
}
public class Fence : Component
{
public int Distance(int FromX, int FromY)
{
int returnValue = 0;
// Caluclate distance...
return returnValue;
}
}
public class Yard : Component
{
// Yada yada yada
}
public class MyCode
{
public List<Component> MyFences;
public MyCode(List<Component> Fences, int FromX, int FromY)
{
// Sort the fences by their distance from specified X,Y
Fences.Sort((A as Fence, B as Fence) => A.Distance(FromX, FromY).CompareTo(B.Distance(FromX, FromY)));
// Or
List<Fence> sortedFences = MyFences.OrderBy(A => A.Distance(FromX, FromY)).ToList();
// Or ???
}
}
Use the Enumerable.Cast<Fence> extension method to transform your IEnumerable<Component> to IEnumerable<Fence>. Then I'd use your second approach (the OrderBy approach) to sort it, but that's my preference.
List<Fence> sortedFences = MyFences.Cast<Fence>().OrderBy(A => A.Distance(FromX, FromY)).ToList();
This approach will throw if there is an object in MyFences that can't be cast to Fence. If you expect that the code should only be passed Fences, this might be what you want. If, instead, you want to skip over non-Fence members, you can use:
List<Fence> sortedFences = MyFences.OfType<Fence>().OrderBy(A => A.Distance(FromX, FromY)).ToList();

Accessing Field from derived Class

I'm trying to access a field from a derived class in an array that holds references to the base class.
I have three classes:
abstract GameObjectBase
{
}
And derived from that are:
public Gamespace: GameObjectBase
{
private bool containsItem;
}
And:
public GameWall: GameObjectBase
{
}
(Obviously these classes hold more data, methods, and constructors).
I have created an array from these objects, like this
private GameObjectBase[,] _labyrinthArray = new GameObjectBase[10,10];
I then fill said array with Gamespaces and Gamewalls. But when I access a Gamespace object in the array, the containsItem field is not accessible due to the reference to the object being of type GameObjectBase.
Obviously I could put containsItem in GameObjectBase and make it accessible from there, but that doesn't fit my OOP approach. The only other solution I have found is to cast the object in question explicitely to Gamespace.
That seems quite crude and error prone to me. Is there any better solution to this?
First of all, you cannot reference a private field from outside the object class itself. You probably want to use a read-only property to encapsulate the field. If you don't want to cast the object explicitly to a Gamespace, you could use an interface instead.
public interface ICanContainItem
{
bool ContainsItem { get; }
}
public class Gamespace : GameObjectBase, ICanContainItem
{
private bool _containsItem;
public bool ContainsItem
{
get { return _containsItem; }
private set { _containsItem = value; }
}
}
This way you can then check whether the object "can contain an item" or not through the interface. Even if in the future you add new types of spaces that can contain an item, this same piece of code works, if the new types also implement the same interface.
var gameObject = _labyrinthArray[i,j]; //i,j defined elsewhere
var mayContainItem = gameObject as ICanContainItem;
if (mayContainItem != null)
{
var itemExists = mayContainItem.ContainsItem;
//mayContainItem.ContainsItem = false; //<-- fails because there's no setter
}

How would I have properties of an object modified only by components of that object in C#?

So let's say I have an interface in my game, IItem. IItem represents an object with a render component, a weight, and an attached "process". The process may be attached by someone using some ability to affect the item. Maybe something like an "enchantment" in an RPG. Let's say I want that attached process to be able to modify the weight of the IItem. However, I only want a process which is a component of the IItem to be able to do so. Objects outside of the IItem need to be able to get the weight though.
Do I implement a Weight property, with just a getter in the interface? Then does the base class implement a "SetWeight" method, which is declared internal to the Item namespace? That still does not really guarantee that only a component owned by the particular IItem can affect it of course, but at least something in the Input namespace cannot directly change the weight of an item. In C I could do something similar to linked lists in the kernel which can get their container, this would give me a way to make sure the weight and the process had the same container object, however, I do not see an easy way to do this in C#. Furthermore, I think it kind of damages the component based design for a component to have to have a reference back to its parent.
This is a general problem I am running in to as I design my interfaces and classes.
Have you considered using a Dictionary, instead of properties? Something like this:
// Item base class
public Dictionary<string, object> Attributes { get; private set; }
public List<Process> Enchantments { get; private set; }
public virtual T Get<T>(string identifier)
{
var att = this.Attributes.FirstOrDefault(att => att.Key == identifier);
if (att == null) throw new MissingAttributeExeption(identifier); // Or perhaps just return default(T)
if ((att.Value is T) == false) throw new InvalidAttributeCastException(identifier, typeof(T));
var value = att.Value;
foreach (var ench in this.Enchantments)
{
ench.Modify(identifier, ref value);
}
return value as T; // Maybe you need to cast value to Object, and then to T, I can't remember.
}
// Process class
public string ValueToModify { get; set }
public virtual void Modify(string identifier, ref object value)
{
if (identifier != this.ValueToModify) return;
// In an inherited class, for example a Weightless-Enchantment: Halfs all weight
var castedVal = value as int
value = castedVal / 2;
// Now this one item weights 50% of normal weight, and the original value is still stored in Item's Attributes dictionary.
}
// Some random class
public void Update()
{
var totalWeight = 0;
foreach (var item in this.itemCollection)
{
int weight = item.Get<int>("Weight");
totalWeight += weight;
}
Console.WriteLine("The player weights: {0}", totalWeight);
}
Obviously this means you can't really hardcode attributes, but... Do you ACTUALLY want to do this, in the long run? I mean once you start adding Intelligence, Agility, Strength, Stamina, Endurance, etc.
I know this doesn't solve your posed question, but I should think it's a pretty decent alternative.
Well, I think I get what you're talking about.
To ensure that your process, e.g. IEnchantment may take an IItem as a dependency(keep it as a member), and have IItem have Update/Set methods that take an IEnchantment, and then you can check that, for example:
public void Update(IEnchantment enchantment)
{
if (enchantment.AttachedItem != this)
throw new Exception("Cannot Update Item!");
/*
* Execute Updating Logics
*/
}
Here's one library I tried designing for my own games, maybe it can help you think about something. It's far from perfect, it's barely anything at all, but I hope it can help in any way.

Type confusion: A type is class, a variable and an object?

I'm currently working through the Pluralsight C# 5.0 course, and I'm relatively new to programming.
I previously thought I understood the concept of Data Types on a basic level, Int/Array/Strings etc.
In this course it starts to introduce C#'s huge emphasis on Types, and creating your own custom types.
One of the course code snippets which I've included below, is refusing to sink in and I was hoping
someone could provide some clarity or a different way of thinking about it.
Program.cs:
GradeStatistics stats = book.ComputeStatistics();
GradeStatistics.cs:
namespace Grades
{
public class GradeStatistics
{
public GradeStatistics()
{
HighestGrade = 0;
LowestGrade = float.MaxValue;
}
public float AverageGrade;
public float HighestGrade;
public float LowestGrade;
}
}
Grades:
public GradeStatistics ComputeStatistics()
{
GradeStatistics stats = new GradeStatistics();
float sum = 0f;
foreach (float grade in grades)
{
stats.HighestGrade = Math.Max(grade, stats.HighestGrade);
stats.LowestGrade = Math.Min(grade, stats.LowestGrade);
sum += grade;
}
stats.AverageGrade = sum / grades.Count;
return stats;
}
I'm finding it particularly difficult to understand what exactly GradeStatistics is.
In the course it is referred to as not only a class, but as a variable, and furthermore also
being returned as an object in Grades.
Any clarity is appreciated, as I'm finding it a little difficult to follow with all of the
above terms being thrown around.
GradeStatistics is a class, from this declaration:
public class GradeStatistics
stats is a variable of type GradeStatistics, from this declaration:
GradeStatistics stats = new GradeStatistics();
The return type of ComputeStatistics is GradeStatistics, from this declaration:
public GradeStatistics ComputeStatistics()
So, it's a class. It is being used to declare a variable with a particular type, and it is used to declare what a particular method will return, an object of the type.
If it helps, you can sort of think of a type as a blueprint. You can have a blueprint of a house. This will tell you that "if you had a house", this is what it would look like.
When you construct an instance of the type, ie. build the house, you get an instance. It has a type (it follows the blueprint), but it may be different from other instances, having other property values (like the color of the paint used, or the style of doors).
First, GradeStatistics is a Class.
Second, it is not referred as a variable anywhere. But stats is actually a variable of type GradeStatistics in this line.
GradeStatistics stats = new GradeStatistics();
Third, ComputeStatistics is a function which return GradeStatistics.
And if you have read OOP than you should know that any object of any class type can be returned as a function return value.
GradeStatics is a class by definition. When you use the new keyword you are creating an instance of that class (an object) which you can pass around or assign it to a variable.
So your snippets above, GradeStatistics is defined as a class in the gradestatics.cs file. Grades.cs creates an instance of this class in the computestatics method and assigns it to the stats variable which is populated in yhat method and then returned
GradeStatistics is a class. From this snippet:
public class GradeStatistics
{
public GradeStatistics()
{
HighestGrade = 0;
LowestGrade = float.MaxValue;
}
public float AverageGrade;
public float HighestGrade;
public float LowestGrade;
}
However, in the method ComputeStatistics, the return type is GradeStatistics, not void as you are probably accustomed to. This means that ComputeStatistics returns a GradeStatistics. Lets look at an easier example.
Suppose we have a class Foo. It is constructed like:
public class Foo
{
public int a;
public int b;
public Foo()
{
a = 0;
b = 0;
}
}
Now we can create Foos and access them normally.
Foo foo = new Foo();
foo.a = 4;
foo.b /= 2;
Now suppose we want to do this, except many times, so we create a method.
public Foo IncrementFoo(int amount, int divideBy)
{
Foo obj = new Foo();
obj.a = amount;
obj.b /= divideBy;
return obj;
}
This method creates a new object of type Foo, and returns it. You can then use this code like:
Foo newFoo = IncrementFoo(4, 2);
//foo == newFoo
I would recommend you read more here, especially about return types.
Note: In this case, it may be better to write an Extension method, especially if we want to modify a single instance of a Foo, but we don't want to get into that. Also if we were really wanting to create a new instance of Foo like that, it would be better to use a constructor.

How do I add multiple objects to a single array?

I am designing a very simple inventory system for a game. I have run into an obstacle where I have the inventory (an array of a specific type) that would need to accept multiple types of objects. My code:
IWeapon[] inventory = new IWeapon[5];
public void initialiseInventory(IWeapon weapon, IArmour armour)
{
inventory[0] = weapon; // Index 0 always contains the equipped weapon
inventory[1] = armour; // Index 1 always contains the equipped armour
}
I would get an error stating that the array can't convert the armour object to a weapon object (which is the array type). I then thought I might make a superclass (well, interface to be precise) that IWeapon and IArmour would inherit from. But then I run into another error...
IItem[] inventory = new IItem[5];
public void initialiseInventory(IWeapon weapon, IArmour armour)
{
inventory[0] = weapon; // Index 0 always contains the equipped weapon
inventory[1] = armour; // Index 1 always contains the equipped armour
Console.WriteLine("The weapon name is: " + inventory[0].Name) // Problem!
}
Since the array type is IItem, it would only contain properties and methods from IItem, and not from IWeapon or IArmour. Thus the problem came in that I could not access the name of the weapon located in the subclass (subinterface) IWeapon. Is there a way I could redirect it somehow to look for properties in a subinterface (IWeapon or IArmour) rather than the superinterface (IItem)? Am I even on the right path?
Since the first item will always be a weapon, and the second will always be armor, you shouldn't use an array (or any data structure) at all. Just have two separate fields, one that holds a weapon and another an armor instance.
private IWeapon weapon;
private IArmour armor;
public void initialiseInventory(IWeapon weapon, IArmour armour)
{
this.weapon = weapon;
this.armor = armor;
}
This is an interesting (and common) puzzle. You have figured out its first part correctly: in order to store the elements in a single array, the array type must match the common ancestor of all elements that go into the array. Of course, this limits the functionality to only what's offered by that common ancestor, which apparently is not enough in your circumstances.
The second part (namely, what to do with elements once you have them all in the array) is a bit harder. You need either a type cast, or a multiple dispatch. The type cast is easy: just add (IWeapon) in front of the element:
((IWeapon)inventory[0]).Name
For multiple items, you can use LINQ:
foreach (IWeapon w in inventory.OfType<IWeapon>()) {
Console.WriteLine("The weapon name is: " + w.Name);
}
Multiple dispatch is a lot more complex. It lets you make methods virtual with respect to more than one object. In return you must sacrifice the simplicity offered by the language: calling methods would require making special objects, rather than calling methods directly. Take a look at the Visitor Pattern for some ideas of how to deal with multiple dispatch.
You can use the is operator to determine if a variable implements a specific interface and then cast that variable to an instance of that interface.
if (inventory[0] is IWeapon)
{
IWeapon myWeapon = (IWeapon)inventory[0];
Console.WriteLine("The weapon name is: " + myWeapon.Name);
}
In the parent class/ interface, you need to decide what common operations/ attributes are truly meant to go here.
It might be worth having the interface something like this:
Interface IItem
{
string name {get};
string itemType {get};
}
then you can just go
foreach(Iitem anItem in itemArray)
{
Console.WriteLine("The " + anItem.itemType + " is: " + anItem.Name);
}
It's not perfect, and raises questions about your model, but it is just something to think about.
Although I totally agree with the answer Servy gave:
If you really really want to use an array (or a List<IITem> ?) you only need to add the Name property to the IItem interface.
interface IItem
{
string Name {get;set;}
}
I doubt if it will help you on the long run, so I'd go for Servy's answer.
I detect a little confusion between interfaces and classes, but nevertheless, you should very simply make sure that IItem has the Name property on it (which, if its an interface, IWeapon and IArmour would need to implement), rather than putting the Name property on each subclass (no such thing as a "subinterface" :) )
Perhaps you should post the code of your interfaces/classes though....
Both weapons and armour have names, so that's a property that should go in the IItem interface.
Also, what Servy says makes sense, having an array for different kind of items doesn't make much sense as the specific positions always have the same type of item.
If you want to access them as an array, you can create a class that both has an array, and lets you access the equipped items with the specific type:
public class Inventory {
public IWeapon CurrentWeapon { get; }
public IArmour CurrentArmour { get; }
private IItem[] _items = new IItem[8];
public IItem[int idx] {
get {
return
idx == 0 ? CurrentWeapon :
idx == 1 ? CurrentArmour :
idx_items[idx - 2];
}
}
}
I have to add my 2 cents, because i found the question quite well asked and it may be a common problem.
I would go with something like this:
interface IItem
{
//some common properties / methods
}
interface IWEapon : IItem
{
string Name { get; set; } //maybe this should go to IItem? depends on your actual objects, of course
//some other wepaon specific properties / methods
}
interface IArmor : IItem
{
//some properties / methods
}
class Inventory
{
public Inventory(IWEapon startWeapon, IArmor startArmor)
{
CurrentWeapon = startWeapon;
CurrentArmor = startArmor;
//optional:
m_items.Add(startWeapon);
m_items.Add(startArmor);
}
private List<IItem> m_items = new List<IItem>();
IEnumerable<IItem> InventoryItems
{
get { return m_items; }
}
void AddItem(IItem item)
{
m_items.Add(item);
}
IWEapon CurrentWeapon
{
get;
set;
}
IArmor CurrentArmor
{
get;
set;
}
}
Why design a class for the inventory? Because you could add things like a TotalWeight or ItemCount property, etc. way more easily than if you have just an array of IItem.
Assuming you really require an array to store the different inventory items (and not just two separate fields) then it seems you could just use inheritance. I took the liberty of adding different properties to Weapon and Armor to clarify the use cases.
Base Class:
abstract class Item
{
public string Name { get; set; }
}
Derived Types:
class Weapon : Item
{
public int Power { get; set; }
}
class Armor : Item
{
public int Resistance { get; set; }
}
Example usage:
Item[] inventory = new Item[2];
inventory[0] = new Weapon { Name = "Singing Sword", Power = 10 };
inventory[1] = new Armor { Name = "Chainmail Shirt", Resistance = 5 };
// Note below that the Name property is always accessible because it is defined in the base class.
// Traverse only Weapon items
foreach (Weapon weapon in inventory.OfType<Weapon>())
{
Console.WriteLine(weapon.Power);
}
// Traverse only Armor items
foreach (Armor armor in inventory.OfType<Armor>())
{
Console.WriteLine(armor.Resistance);
}
// Travers all items
foreach (Item item in inventory)
{
Console.WriteLine(item.Name);
}

Categories