阴影字段未在基类构造函数

时间:2016-04-26 17:43:08

标签: c#

为什么当我在基类构造函数中设置一个带阴影的字段(使用new关键字声明)时,被遮蔽的字段会被设置但不会被遮挡的字段?

我认为this.GetType()一直指向最外层的类,包括构造函数。我还认为阴影使阴影场无法进入。

在我的快速观察中,我可以看到两个字段,已设置阴影的字段和尚未初始化的阴影(子类)。

我通过在调用基类构造函数后在子类构造函数中显式设置阴影字段来修复它,但我仍然想知道它为什么会这样做。 .Net Fiddle

using System;

public class Program
{
    public static void Main()
    {
        SubClass subClass = new SubClass(2);
        Console.WriteLine(subClass.MyField);
    }
}

public class BaseClass
{
    public BaseClass(int value)
    {
        MyField = value; // This doesn't point to SubClass.MyField
    }

    public int MyField;
}

public class SubClass : BaseClass
{
    public SubClass(int value):base(value)
    {

    }

    public new int MyField = 4;

}

更新

在查看答案后,我发现我并没有以最直接的方式询问我想知道的事情。任何不便敬请谅解。这是我真正想知道的:

我确实理解阴影。我不同意。我不认为应该允许字段(只要可覆盖的字段是语言功能)。我没有看到阴影场中的那一点,并且阴影场徘徊。然而,我确实看到了可覆盖领域的重点,我不明白为什么语言特征在属性和方法存在时不存在。那么,为什么要在场上遮蔽呢?为什么没有覆盖领域?

4 个答案:

答案 0 :(得分:7)

我们有

  

我还认为阴影使阴影场无法进入。

其次是

  

我确实理解阴影。

我不完全确定你这样做。你已经表达了一个关于阴影的错误信念;我们怎么知道还没有更多?

  

我不同意。

您的意见得到了注意。我注意到,如果您不喜欢,则不需要使用该功能。

  

我不认为应该允许字段(只要可覆盖的字段成为语言功能)。

注意到。

  

然而,我确实看到了可覆盖字段中的点,我不明白为什么当属性和方法存在时,该语言特征不存在。

字段应该是类的私有实现细节。如果是,那么阴影或覆盖没有可访问的名称,所以问题没有实际意义。我们希望没有人使用的功能是一个糟糕的功能。

  

那么,为什么要在字段上进行遮蔽?

假设派生类中有受保护的字段。现在考虑脆弱的基类问题。通过一些这样的场景; 你批评了语言设计选择,所以像语言设计师一样思考。您对典型脆弱基类场景的调查得出的结论是什么?

  

为什么没有覆盖字段?

因为(1)我们没有覆盖字段的机制;方法有vtable但是字段没有vtable机制。并且(2)字段应该是类的私有实现细节,因此永远不需要覆盖它们。如果要覆盖字段,请创建一个包装它的属性并覆盖它。代码更改 tiny 从字段转到虚拟属性。

字段应位于类的机制域中;属性在业务域中。行为专业化属于业务领域。

答案 1 :(得分:2)

public class BaseClass
{
    public BaseClass(int value)
    {
        MyField = value; // This doesn't point to SubClass.MyField
    }

    public int MyField;
}

基类不了解其派生类型。 MyField = value完全按照它所说的做法:它为MyField分配value

public class SubClass : BaseClass
{
    public SubClass(int value):base(value)
    {

    }

    public new int MyField = 4;

}

您期望值4传播到基本类型吗?你的问题很难回答,因为它并不完全清楚你对#34;直观"的定义。和期望是....而你的示例代码是非常糟糕的OOP设计。

没有充分的理由可以公开public字段,基类是不是。

让我重新说一下你的例子:

public abstract class BaseClass
{
    protected BaseClass(int value)
    {
        _value = value;
    }

    private int _value;
    public virtual int Value { get { return _value; } set { _value = value; } }
}

public class SubClass : BaseClass
{
    public SubClass(int value) : base(value)
    {

    }

    public new int Value { get; set; } // hides base class member
}

现在。 SubClass.Value是它自己的东西 - 如果你想访问在构造函数中传递的值,你需要通过基类来实现。

基类成员不会停止存在",它只是隐藏阴影,派生类型中的新成员,恰好具有相同的标识符:

var foo = new SubClass(42);
Console.WriteLine(foo.Value);
Console.WriteLine(((BaseClass)foo).Value);

Console.ReadKey();

此代码输出0,然后42 - 因为当foo.Value被访问时SubClass说"嘿基类,让我来处理 - 我有我自己对Value的定义,这个叫我的选择。" ...并返回0因为,它实际上从未被分配过; 42只能通过基类显示。

成员影子的作用。

即使没有new关键字也能正常工作 - new关键字只会抑制编译器警告说“你在这里隐藏基类成员”,你真的完全确定你打算这样做吗?" - 因为在正常的世界中,这通常不是你想要做的事情。

现在,请注意我创建了Value属性virtual。如果你改为override会发生什么?

public class SubClass : BaseClass
{
    public SubClass(int value)
        : base(value)
    {

    }

    public override int Value { get; set; } 
}

上面的小控制台程序(几个片段)现在将输出00 - 因为子类' Value 覆盖基类成员,有效地告诉C#解析对派生类型的成员调用。因此,要输出您传入的42 ,因为您要覆盖成员,您需要对其工作原理负责 - _value私有字段在基类中仍然显示42,但覆盖了Value属性,该字段未被使用。

所以你在派生类型的构造函数中赋值(这里密封类以避免构造函数中的虚拟成员调用):

public sealed class SubClass : BaseClass
{
    public SubClass(int value)
        : base(0)
    {
        Value = value;
    }

    public override int Value { get; set; } 
}

因此,这里派生类型将0传递给基类,并覆盖成员。现在42的小片段输出是什么?

    static void Main(string[] args)
    {
        var foo = new SubClass(42);
        Console.WriteLine(foo.Value);
        Console.WriteLine(((BaseClass)foo).Value);

        Console.ReadKey();
    }
  

它将为派生调用和下调调用输出42,因为类型转换现在是多余的,因为虚拟成员被覆盖。

答案 2 :(得分:0)

不确定你为什么要这样做,但这是如何:

public class BaseClass
{
    public BaseClass(int value)
    {
        SetValue(x => x.MyField, value);
    }

    public int MyField;

    public void SetValue<TField>(Expression<Func<BaseClass, TField>> memberSelector, TField value)
    {
        var expression = memberSelector.Body as MemberExpression;
        if (expression == null)
            throw new MemberAccessException();

        this.GetType().GetField(expression.Member.Name)
            .SetValue(this, value);
    }
}

答案 3 :(得分:0)

请记住,遮蔽字段只会创建一个新字段,它与基类中的字段无关(除非您已经给它指定了相同的名称)。< / p>

简单地说,您还没有编写任何设置SubClass字段的代码,您只编写了在BaseClass中设置字段的代码:

public class BaseClass
{
    public BaseClass(int value)
    {
        MyField = value; // This doesn't point to SubClass.MyField
    }

    public int MyField;
}

MyField此处只能引用MyField中的BaseClass。它可能不知道您以后要创建一个子类并在其中放置一个具有相同名称的字段。即使你这样做,你所做的只是创建一个具有相同名称的不同字段,那么为什么设置BaseClass.MyField也会设置大部分不相关的SubClass.MyField