1

在开发通过 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.NullReferenceException 在 D:\Code\c#\Test\Program.cs:line 12 中的 Test.Program.Main(String[] args) 处未处理

.NET 版本:4.0

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

为什么会这样?我的意思是,除非它是一个错误,否则它背后可能有一个设计原理。什么样的情况会保证 CLR 在向上转换后将实例的先前非空成员视为空?

4

3 回答 3

6

当您使用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.
于 2013-06-01T17:14:13.867 回答
4

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

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

您在这里使用 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 = "";
        }
    }
于 2013-06-01T17:17:32.683 回答
2

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

这篇文章 -

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

于 2013-06-01T17:21:46.267 回答