Marton
Marton

Reputation: 831

C# inherited field value getting mixed up with base class' field value

I'm trying to serialize instances of 'MyClass' into an XML file, one at a time. Each instance has an int property called 'ID'. The target file already has some items of the same type, and I'm appending the new items after those. I would like the to-be-inserted instance's ID value to be 1 higher than the largest existing item in the XML.

I have the following generic class:

public class ContentDataManager<T> where T : MyBaseClass
{
    private static string _contentLocation = "Content/Data/";
    private const string DEFAULT_FILEPATH = "data.xml";

    public static int GetNextId(string filepath = DEFAULT_FILEPATH)
    {
        var allData = DeserializeAll(filepath);
        int largestId = 0;
        foreach (T data in allData)
        {
            if (data.ID > largestId)
            {
                largestId = data.ID;
            }
        }
        return largestId + 1;
    }
//...
}

I'm using this class like this:

public class MyClass : MyBaseClass
{
    public string Name;
    public float Size;
    public new int ID;

    public static MyClass CreateNew(string name, float size)
    {
        MyClass newClass = new MyClass();
        newClass.Name = name;
        newClass.Size = size;
        newClass.ID = ContentDataManager<MyClass>.GetNextId(DefaultFilepath);
        return newClass;
    }

MyBaseClass looks like this:

public abstract class MyBaseClass
{
    //...
    [ContentSerializerIgnore]
    public int ID = 0;
}

The problem is, that in the ContentDataManager.GetNextId(...) method's foreach loop, there's an if statement that actually doesn't work properly. When I was debugging, I added two watches: 'data' and 'data.ID' Here comes the interesting part: The watch for 'data' shows that the ID property's value is 1. At the sime time, the 'data.ID' property shows a value of 0.

I'm pretty sure this error is related to inheritance. How should I change my code, so that I don't get this error?

Upvotes: 1

Views: 1038

Answers (2)

Andrew Savinykh
Andrew Savinykh

Reputation: 26300

You need to remove this line:

public new int ID;

From what you shown there is no need for a separate ID in the derived class, the base one will do just fine. Of course you will need to removed the ContentSerializerIgnore attribute too if you want ID to be serialized.

Here is an example to demonstrate, that when you use the 'new' keyword for declaring a member you create completely unrelated member to the base class member.

using System;

namespace Test
{
    abstract class  Base
    {
        public string Data;
    }

    class Derived : Base
    {
        // this is a separate field, has nothing in common with the base class field but name
        new public string Data;
    }

    class Program
    {
        static void Main(string[] args)
        {
            Derived test = new Derived();

            //Let's set the Data field in the derived class
            test.Data = "Derived";

            //Now let's set this field in the base class
            Base cast = test;
            cast.Data = "Base";

            //We can see that the feild in the derived class has not changed
            //This will print 'Derived'
            Console.WriteLine(test.Data);

            // Just to make sure that a new object has not been constructed by a miracale
            // let's pass our object to a function that will display the Data field from
            // the base class
            Test(test);
        }

        static void Test(Derived test)
        {
            // When called from the Main above this will print 'Base'
            Console.WriteLine(((Base)test).Data);            
        }
    }
}

Upvotes: 2

Peter K.
Peter K.

Reputation: 8108

The issue is that you've specified where T : MyBaseClass. And that means the data foreach loop variable will be of type MyBaseClass. So the only accessible value of data.ID will be the base class's version.

See this write-up about it.

You'll have to either type cast data to be type MyClass or always use the base class ID field (which is what zespri suggests).

Upvotes: 1

Related Questions