我有以下代码:
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 :(得分:25)
来自ECMA-334(C#规范)§10.7.1.2:
新成员的声明仅在新成员的范围内隐藏继承的成员。
您可以通过在Microsoft的实现上运行此测试来查看此行为。
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中),因为新的派生成员只隐藏受保护范围内的“旧”基础成员。
根据规范,Mono在这里做错了。
答案 1 :(得分: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,它有一些阻止它们的bug修复。 他们的构建是否会因为调用Blah(Bar 上的内部方法)而中断?当然不是。那将是可怕的。此更改是私有实现详细信息,应该在Bar.DLL之外不可见。
答案 2 :(得分:5)
一般来说,C#的.NET实现应该被认为是“佳能”。来自new Modifier的文档:
在类或结构中引入的常量,字段,属性或类型使用同名隐藏所有基类成员。< / p>
......根据这个定义,似乎Mono实现更正确。它应该隐藏MyField
类中Parent
的实现,因此只能使用int MyField
类中的Child
签名访问它。
答案 3 :(得分:3)
序曲:此代码很疯狂。如果您的应用中确实有这样的代码,请立即修复。要么让它们受到保护,要么两者都公开!
关于错误:CLR在处理这样的事情时有很多非常奇怪的“边缘情况”规则。寻找这类东西的最佳地方通常是Eric Lippert's blog。
但是,在我看来,在我看来,单声道看起来实际上是在做更明智的事情。
再看一下,一旦你考虑到“幕后”的东西,C#one会更有意义。
MSIL中的属性不是“第一类”。 C#或VB中的属性只是编译成get和set方法(编译器还在某个地方粘贴一个属性用于簿记)。
int MyField { get; set; }
实际上会为两种方法生成MSIL:
void set_MyField(int value);
int get_MyField();
现在,鉴于您的new
方法具有不同的类型,您最终会得到以下两种setter方法。
void set_MyField(int value);
void set_MyField(string value);
当你致电x.MyField = "string"
时,你只是在调用其中一种方法。然后归结为正常的方法重载方案。有两个同名的方法采用不同的参数是完全有效的,所以编译器只需选择一个字符串并继续它的快乐方式。
所以是的。如果你知道内部是如何工作的话,C#one是有意义的,如果你没有,那么Mono更有意义。
哪一个“更正确”?问Eric Lippert: - )
答案 4 :(得分:2)
只需加上我的2美分)这是一个单声道错误,here就是描述。
答案 5 :(得分:0)
恕我直言,区别在于MS.NET识别MyField
的类型字符串并设置Parent
属性的值,而在Mono中只是尝试访问MyField
中的Child
} class。
答案 6 :(得分:0)
你正在通过孩子无法通过基类提供的东西。你可以尝试,但它实际上不会做任何事情。人们总能做到这一点:
Parent child = new Child();
并调用该方法。因此,如果您希望隐藏字段,请声明一个新字段并将继承的字段保持公开。