-2

I have a class named Item which acts as an abstract class, but is not defined as one. This class has a MaxPerStack property:

public class Item
{
    public short MaxPerStack
    {
        get
        {
            return this.Data.MaxPerStack;
        }
    }
}

I also have a class named Equip which derives from the Item class and also has a property named MaxPerStack which is declared as new:

public class Equip : Item
{
    public new short MaxPerStack
    {
        get
        {
            return 1;
        }
    }
}

I have the following method called Add, which uses the MaxPerStack property of item:

public void Add(Item item)
{
    Console.WriteLine("Stack: {0}.", item.MaxPerStack);
}

When I do:

Add(new Equip());

It goes to the MaxPerStack property of Item rather to the Equip one.

Why's that happening?

Kit
  • 20,354
  • 4
  • 60
  • 103
Gilbert Williams
  • 970
  • 2
  • 10
  • 24
  • 2
    you should use a real abstract class and/or virtual methods. – Daniel A. White Jul 01 '15 at 16:43
  • 1
    http://stackoverflow.com/questions/6576206/what-is-the-difference-between-the-override-and-new-keywords-in-c – Daniel A. White Jul 01 '15 at 16:44
  • @DanielA.White I cannot make `Item` abstract as I'm creating instances of this class. – Gilbert Williams Jul 01 '15 at 16:44
  • 1
    `I have a class named Item which acts as an abstract class, but is not defined as one.` why? `It goes to the MaxPerStack property of Item rather to the Equip one.` that's because that's what you told it to do. All it knows is that `item` is an `Item`, it doesn't know it's an `Equip`. And since `MaxPerStack` isn't virtual, it calls the one in `Item`. – Matt Burland Jul 01 '15 at 16:44
  • 2
    @GilbertWilliams then you should make it `virtual` – Daniel A. White Jul 01 '15 at 16:44
  • You need to show us more info. What class owns the `Add(Item)` method. Show us how you are isntancing the object, so we can see what object you are calling `.Add` on. – Johnathon Sullinger Jul 01 '15 at 16:45
  • 1
    @GilbertWilliams if you are creating instances, then you need to make an even higher class, which then is really abstract. – SQL Police Jul 01 '15 at 16:46
  • 2
    You are confusing the concept of abstract. Don't call `Item` an abstract class, as abstract means it can not be instanced and it potentially contains members that must be implemented. Instead, call it what it is - a base class. – Johnathon Sullinger Jul 01 '15 at 16:47
  • @JohnathonSullinger None of the information you've said is missing is relevant or necessary to explain the behavior in question. – Servy Jul 01 '15 at 16:48
  • @JohnathonSullinger He specifically shows the call site of the method: `Add(new Equip());`, so we know that the parameter really is an `Equip` instance. We don't need to know anything about the type that the `Add` method is in, as none of the other members of that class, or any details about the class itself, are going to affect the behavior shown here, and there is already enough information to explain the behavior seen. One could make the example a *touch* better by removing that not-named type from the example entirely, but what's there is fine. – Servy Jul 01 '15 at 16:54
  • @Servy I misread the implementation of the `Add` method, my mistake. Thanks for pointing it out. – Johnathon Sullinger Jul 01 '15 at 16:56

2 Answers2

5

Because you didn't mark Item.MaxPerStack as virtual, and you didn't mark Equip.MaxPerStack as override. You need to do both of those things to get the behavior you're expecting.

By implicitly marking the base type's method as sealed and explicitly marking the derived type as new you're preventing virtual dispatch, which is the exact mechanism that is used to ensure that a method called on the base type will execute the implementation associated with the actual runtime type of the object, rather than statically binding the method to the implementation in the compile time type of the object.

Servy
  • 202,030
  • 26
  • 332
  • 449
1

The problem here is that your Equip class replaces the MaxPerStack property defined on the Item class.

public class Equip : Item
{
    public new short MaxPerStack
    {
        get
        {
            return 1;
        }
    }
}

MSDN tells you that:

If the method in the derived class is preceded with the new keyword, the method is defined as being independent of the method in the base class.

..

Using the new keyword tells the compiler that your definition hides the definition that is contained in the base class. This is the default behavior.

This applies to properties as well. This tells you that the Equip will have it's own, non-related, version of MaxPerStack. So when you call Item.MaxPerStack you are accessing the property on the Item class, and not the Equip class. Regardless if the Item you pass in is actually an Equip class.

To solve this, you can make the MaxPerStack property virtual, then override it in the Equip class.

public class Item
{
    public virtual short MaxPerStack
    {
        get
        {
            return this.Data.MaxPerStack;
        }
    }
}

public class Equip : Item
{
    public override short MaxPerStack
    {
        get
        {
            return 1;
        }
    }
}

Now when you provide Equip as the parameter to Add(Item), the Add method will access the MaxPerStack property on the Equip class instead.

MSDN tells you this:

If the method in the derived class is preceded with the override keyword, objects of the derived class will call that method instead of the base class method.

Johnathon Sullinger
  • 7,097
  • 5
  • 37
  • 102