为什么c#在向上转换后将实例中先前非空的成员视为空?

本文关键字:成员 转换 实例 为什么 | 更新日期: 2023-09-27 18:16:28

在开发一个通过JSON与外部数据库通信的类库时,出现了一个有趣的问题:对象实例的上转换导致其先前的非空成员之一显示为空。

为了找出这个奇怪现象的原因,我已经绞尽脑汁了,但到目前为止,我还没能找到一个合理的解释。

下面是这个问题的一个例子:

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 = "";
        }
    }
}

错误信息:System。在Test.Program中未处理NullReferenceException。Main(String[] args) in D:'Code'c#'Test'Program.cs:line 12

。NET版本:4.0

环境:64位Windows 2008 R2, Visual Studio 2010

为什么会发生?我的意思是,除非它是一个bug,否则它背后可能有一个设计原理。在哪种情况下CLR会在向上转换之后将实例的先前非空成员视为空?

为什么c#在向上转换后将实例中先前非空的成员视为空?

当您使用new修饰符时,您将隐藏继承的属性。

public new DescendantAttributes Attributes;

看下面的链接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.

C#中,new关键字可以用作操作符修饰符约束。MSDN链接

  • new operator:用于在堆上创建对象并调用构造函数。
  • new修饰符:用于隐藏基类成员的继承成员。
  • new constraint:用于限制可能用作泛型声明中类型形参参数的类型。

您在这里使用new作为修饰符

new modifier用于显式隐藏从基类继承的成员。在您的代码中,您正在跟踪Attributes,它在该层次结构级别提供了不同的实现。正如Dirk所说,你正在创建一个新的不相关的属性

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 = "";
        }
    }

您需要的不是shadowing,而是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 -

假设您有一个基类,并且您在所有的代码而不是继承的类,并且您使用影子,它将返回基类返回的值,而不是跟随对象实类型的继承树。