2010-05-16 43 views
29

我有以下代码:C#子类中的受保护属性应该隐藏对父级公共属性的访问权吗?

public class Parent 
{ 
    public string MyField { get; set; } 
} 

public class Child : Parent 
{ 
    protected new int MyField { get; set; } 
} 

我试着和访问此:

static void Main(string[] args) 
{ 
    Child child = new Child(); 
    child.MyField = "something"; 
} 

Visual Studio 2008的编译此不加评论,但在Mono下(2.4.2,Ubuntu的),我得到的错误消息

'HideTest.Child.MyField' is inaccessible due to its protection level (CS0122) 

是一个实现还是其他更符合标准?

编辑:感谢所有指出了坏设计的人。不幸的是,它是一个第三方库,并且对其进行显着改变并不实际。

+0

这实际上是否与继承有关? Ubuntu可以使用Child类中的任何受保护字段吗? – 2010-05-16 20:15:30

+2

在回答之前,你能否向我解释一下,如何在Parent类中公开一个属性,并在Child类中保护属性?我认为你有严重的设计问题。 – 2010-05-16 20:20:03

+3

使用'新'是一个要避免的混乱,特别是当你有两个类的源代码的控制。而你正在试图做的事情,即覆盖一个不同类型的遗产,味道真的很差。腐烂的鱼臭,甚至。 ;-)你应该再看看你正在努力完成什么,并找到一个干净的方式来做到这一点。在生命早期形成像这样的不良习惯将导致牛皮癣和焦虑症的发作。 – 2010-05-16 21:00:26

回答

25

ECMA-334(C#的规格)§10.7.1.2:

A declaration of a new member hides an inherited member only within the scope of the new member.

您可以通过运行微软实现这个测试看到此行为。

using System; 
using NUnit.Framework; 

namespace ScratchPad 
{ 
    [TestFixture] 
    public class Class1 
    { 
     [Test] 
     public void InheritanceHiding() 
     { 
      var b = new Base(); 
      var d = new Derived(); 

      var baseSomeProperty = b.SomeProperty; 
      var derivedSomeProperty = d.SomeProperty; 

      b.GetSomeProperty(); 
      d.GetSomeProperty(); 
     } 
    } 

    public class Base 
    { 
     public string SomeProperty 
     { 
      get 
      { 
       Console.WriteLine("Getting Base.SomeProperty"); 
       return "Base.SomeProperty"; 
      } 
     } 

     public string GetSomeProperty() 
     { 
      return SomeProperty; 
     } 
    } 

    public class Derived : Base 
    { 
     protected new int SomeProperty 
     { 
      get 
      { 
       Console.WriteLine("Getting Derived.SomeProperty"); 
       return 3; //Determined by random roll of the dice. 
      } 
     } 

     public new int GetSomeProperty() 
     { 
      return SomeProperty; 
     } 
    } 
} 

将输出:

Getting Base.SomeProperty //(No Controversy) 
Getting Base.SomeProperty //(Because you're calling from public scope and the new member is in protected scope, there is no hiding) 
Getting Base.SomeProperty //(No Controversy) 
Getting Derived.SomeProperty //(Now because you're calling from protected scope, you get the protected member). 

所以你从你的Main()访问应该是基类属性(因为它是在MS.NET)的财产,不是派生属性(如在单声道),因为新的派生成员只隐藏受保护范围内的“旧”基本成员。

根据规范,Mono在这里做错了事。

+0

@Eric Lippert:虽然我认为这是自我一致的,但有些人可能会认为这里存在不和谐。你介意添加一个解释,为什么MS.NET行为比单声道行为更好?我的大脑现在无法提出为什么这是好的,我认为这会使答案更加完整... – 2010-05-17 00:46:19

+0

优秀的问题。见下文。 – 2010-05-17 21:35:06

0

恕我直言,所不同的是MS.NET识别字符串类型为MyField,并设置Parent属性的值,并以单在短短试图访问MyFieldChild类。

0

您正在通过子类不可用的基类获得可用的东西。你可以试试,但它实际上不会做任何事情。人们总是可以这样做:

Parent child = new Child();

并调用该方法。所以如果你想隐藏这个字段,请声明一个新字段,并保留继承的字段。

5

一般来说,C#的.NET实现应该被认为是“canon”。从上new Modifier文档:

A constant, field, property, or type introduced in a class or struct hides all base class members with the same name.

...好像Mono的实施给予这个定义是比较正确的。它应该隐藏Parent类中的MyField的实现,因此只能使用Child类中的int MyField签名才能访问。

+0

你的报价是绝对正确的,但另一部分规范发挥作用(请参阅我的答案)。 – 2010-05-16 21:19:01

+0

啊,很好的解释。 MSDN文档可能会更清晰一点。 – 2010-05-17 00:06:57

3

前奏:这段代码很疯狂。如果你真的在你的应用中有这样的代码,现在就修复它。要么让他们都受到保护,要么都是公开的!

关于错误:CLR有很多非常奇怪的'边缘情况'规则来处理这样的事情。寻找这种东西的最佳地点通常是Eric Lippert's blog

尽管如此,看起来mono在我看来实际上是在做更明智的事情。


第二次看,一旦你考虑到“幕后”的东西,C#更有意义。

MSIL中的属性不是“头等”。 C#或VB中的一个属性只是编译成一个get和set方法(编译器还在某处为某个簿记添加了一个属性)。

int MyField { get; set; }实际上会产生MSIL的两种方法:

void set_MyField(int value); 
int get_MyField(); 

。现在,因为你的new方法有不同的类型,你会用下列2种setter方法结束。

void set_MyField(int value); 
void set_MyField(string value); 

当你调用x.MyField = "string"你只是调用这些方法之一。然后这归结为一个正常的方法重载方案。具有两个具有不同参数的名称相同的方法是完全有效的,因此编译器只需选择一个字符串并继续进行即可。

所以是的。如果你知道内部工作是如何工作的话,那么C#就是有意义的,如果你不知道,那么单声道更有意义。

哪一个“更正确”?问埃里克利珀:-)

+0

这不是我的代码,它是第三方库。真正的代码没有那么奇怪,因为它对于不同的属性没有不同的类型(这是我介绍的用于说明发生了什么的),以及属性共享实现的两个版本。 IIRC派生类中属性的类型是父类中属性类型的子类。 – 2010-05-16 22:58:07

18

杰森的回答是正确的,但他要求为此行为辩解。 (即隐藏方法只隐藏在隐藏方法的范围内)。

有许多可能的理由。其中之一就是this is yet another way in which the design of C# mitigates the Brittle Base Class problem

FooCorp使得Foo.DLL:

public class Foo 
{ 
    public object Blah() { ... } 
} 

BarCorp使得Bar.DLL:

public class Bar : Foo 
{ 
    // stuff not having to do with Blah 
} 

ABCCorp使得ABC.EXE:

public class ABC 
{ 
    static void Main() 
    { 
     Console.WriteLine((new Bar()).Blah()); 
    } 
} 

现在BarCorp说:“你知道,在我们的内部代码中,我们可以保证Blah只会返回字符串,这要归功于我们对派生实现的了解这是我们内部代码中的优势。“

public class Bar : Foo 
{ 
    internal new string Blah() 
    { 
     object r = base.Blah(); 
     Debug.Assert(r is string); 
     return (string)r; 
    } 
} 

ABCCorp拿起Bar.DLL的新版本,它有一堆错误修复程序阻止它们。 他们的构建是否因为他们打电话给Blah而失败,这是内部方法吧?当然不是。那将是可怕的。此更改是一个私有实现细节,它应该是Bar.DLL以外的隐形

+15

当Eric Lippert告诉你你是对的时,你知道你过得很愉快。感谢您回答蝙蝠信号:) – 2010-05-17 22:21:04

2

只需加我2美分)这是一个单声道错误,here是说明。