1

While developing a class library for communicating with an external database over JSON, an interesting problem arose where an upcast of an object instance resulted in one of its previously non-null members appear as null.

Many a hairs have been torn out while trying to figure out the cause of this oddity but so far I have been unsuccessful in finding a sane explanation.

Here is an example of the issue:

using System;
namespace Test
{
    public class Program
    {
        static void Main(string[] args)
        {
            Descendant d = new Descendant();
            d.Attributes.ToString(); // Everything fine here
            Ancestor a = (Ancestor)d;
            a.Attributes.ToString(); // NullPointerException
        }
    }

    class Ancestor
    {
        public interface IAttributes { }
        public IAttributes Attributes;
    }

    class Descendant : Ancestor
    {
        public new DescendantAttributes Attributes;

        public Descendant()
        {
            this.Attributes = new DescendantAttributes();
        }

        public class DescendantAttributes : IAttributes
        {
            public string Name = "";
        }
    }
}

Error message: System.NullReferenceException was unhandled at Test.Program.Main(String[] args) in D:\Code\c#\Test\Program.cs:line 12

.NET version: 4.0

Environment: 64-bit Windows 2008 R2, Visual Studio 2010

Why does it happen? I mean, unless its a bug there probably is a design rationale behind it. What kind of situations would warrant CLR to consider a previously non-null member of an instance as null after an upcast?

Saul
  • 17,973
  • 8
  • 64
  • 88
  • 3
    Because you Descendant class explicitly shadows the Attributes property, thus creating a new and unrelated property. Take a look at [this post that explains the difference between shadowing and overloading](http://stackoverflow.com/questions/392721/difference-between-shadowing-and-overriding-in-c). – Dirk Jun 01 '13 at 17:14
  • Have you tried initializing `Attributes` in `Ancestor`, too? – AntonioJunior Jun 01 '13 at 17:14

3 Answers3

6

When you use new modifier you are hiding the inherited property.

public new DescendantAttributes Attributes;

Look at the below link http://msdn.microsoft.com/en-us/library/vstudio/435f1dw2.aspx

When used as a modifier, the new keyword explicitly hides a member that's inherited from a
base class. When you hide an inherited member, the derived version of the member replaces the
base-class version. You can hide members without using the new modifier, but the result is a
warning. If you use new to explicitly hide a member, the modifier suppresses this warning and
documents the fact that the derived version is intended as a replacement.
cheedep
  • 937
  • 6
  • 19
4

In C#, the new keyword can be used as an operator , as a modifier or a constraint. MSDN Link

  • new operator : Used to create objects on the heap and invoke constructors.
  • new modifier : Used to hide an inherited member from a base class member.
  • new constraint : Used to restrict types that might be used as arguments for a type parameter in a generic declaration.

You are using new as a modifier here.

The new modifier is used to explicitly hide a member inherited from a base class.In your code, you are shadowing Attributes which provides a different implementation at that level of the hierarchy. So as told by Dirk, you are creating a new and unrelated property

class Ancestor
    {
        public interface IAttributes { }
        public IAttributes Attributes;
    }

    class Descendant : Ancestor
    {
        public new DescendantAttributes Attributes;  //Shadowing using new modifier

        public Descendant()
        {
            this.Attributes = new DescendantAttributes();
        }

        public class DescendantAttributes : IAttributes
        {
            public string Name = "";
        }
    }
Bhushan Firake
  • 9,338
  • 5
  • 44
  • 79
2

Instead of shadowing, you need overriding -

class Ancestor
{
    public interface IAttributes { }
    public virtual IAttributes Attributes { get; set; } // Make this virtual
}

class Descendant : Ancestor
{
    // Override here OR instead you can omit the declaration at all.
    public override IAttributes Attributes { get; set; }

    public Descendant()
    {
        this.Attributes = new DescendantAttributes();
    }

    public class DescendantAttributes : IAttributes
    {
        public string Name = "";
    }
}

From this post -

Suppose you have a base class and you use the base class in all your code instead of the inherited classes, and you use shadow, it will return the values the base class returns instead of following the ineritance tree of the real type of the object.

Community
  • 1
  • 1
Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • Oops..!! Yeah you can't have override on field. Edited to make it a property. – Rohit Vats Jun 01 '13 at 17:28
  • Unfortunately I cannot use `IAttributes` as type identifier in `Descendant`, it has to be `DescendantAttributes` as other parts of code need to be able to access its members. – Saul Jun 01 '13 at 17:32