tags:

views:

1011

answers:

7

I have the following code:

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

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

I try and access this with:

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

Visual studio 2008 compiles this without comment, but under Mono (2.4.2, Ubuntu) I get the error message

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

Is one implementation or the other more compliant with the standard here?

Edit: Thanks to all the people who have pointed out the bad design. Unfortunately it's a third-party library and changing it significantly isn't practical.

A: 

IMHO the difference is that MS.NET recognize the type string for MyField and sets the value of Parent property and in Mono in just tries to access MyField in Child class.

Incognito
A: 

You are making something that's available through the base class unavailable through the child. You can try that, but it won't actually do anything. People can always just do this:

Parent child = new Child();

and call the method. So if you want the field to be hidden, declare a new one and keep the inherited one public.

Jouke van der Maas
+5  A: 

In general, the .NET implementation of C# should probably be considered "canon". From the documentation on the new Modifier:

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

... it seems like the Mono implementation is more correct given this definition. It should be hiding the implementation of MyField in the Parent class, and therefore it should only be accessible with the int MyField signature from the Child class.

James Kolpack
Your quote is absolutely correct, but another piece of the spec comes into play (see my answer).
Jason Punyon
Ah, nice explanation. The MSDN doc could probably be a bit more clear on this.
James Kolpack
+3  A: 

Prelude: This code is crazy. If you actually have code in your app like this, fix it now. Either make them both protected or both public!

Regarding the error: The CLR has a lot of really strange 'edge case' rules in it for dealing with things like this. The best place to look for this kind of stuff is usually Eric Lippert's blog.

In saying that though, it looks like mono is actually doing the more sensible thing here in my opinion.


On second look, the C# one makes more sense once you factor in the 'behind the scenes' stuff.

Properties are not "first class" in MSIL. A property in C# or VB is just compiled down to a get and set method (the compiler also sticks an attribute somewhere for bookkeeping).

int MyField { get; set; } will actually produce MSIL for two methods:

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

Now, given that your new method has a different type, you'll end up with the following 2 setter methods.

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

When you call x.MyField = "string" you're just calling one of those methods. This then boils down to a normal method overloading scenario. It's perfectly valid to have two methods with the same name that take different parameters, so the compiler will just select the string one and carry on it's merry way.

So yeah. The C# one makes sense if you know how the internals work, the Mono one makes more sense if you don't.

Which one is "more correct"? Ask Eric Lippert :-)

Orion Edwards
It's not my code, it's a third-party library. The real code is less bizarre, in that it doesn't have different types for the different properties (that was something I introduced to clarify what was going on), and the two versions of the property share implementation. IIRC the type of the property in the derived class is a subclass of the type of the property in the parent class.
Tim
+23  A: 

From ECMA-334 (the C# spec) §10.7.1.2 :

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

You can see this behavior by running this test on Microsoft's implementation.

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

Which will output:

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).

So the property you're accessing from your Main() should be the base class property (as it is in MS.NET), not the derived property (as in Mono), because the new derived member only hides the 'old' base member in protected scope.

Mono is doing something wrong here according to the spec.

Jason Punyon
@Eric Lippert: While I think this is self consistent some might think that there is a dissonance here. Would you mind adding an explanation as to why the MS.NET behavior is better than the Mono behavior? My brain can't come up with the why this is good right now and I think that would make the answer more complete...
Jason Punyon
Excellent question. See below.
Eric Lippert
+17  A: 

Jason's answer is correct but he asks for a justification of this behaviour. (Namely that a hiding method is only hiding within the scope of the hiding method.)

There are a number of possible justifications. One in particular is that this is yet another way in which the design of C# mitigates the Brittle Base Class problem.

FooCorp makes Foo.DLL:

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

BarCorp makes Bar.DLL:

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

ABCCorp makes ABC.EXE:

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

Now BarCorp says "You know, in our internal code we can guarantee that Blah only ever returns string thanks to our knowledge of our derived implementation. Let's take advantage of that fact in our internal code."

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

ABCCorp picks up a new version of Bar.DLL which has a bunch of bug fixes that are blocking them. Should their build break because they have a call to Blah, an internal method on Bar? Of course not. That would be terrible. This change is a private implementation detail that should be invisible outside of Bar.DLL.

Eric Lippert
You know you're having a good day when Eric Lippert tells you you're right. Thanks for answering the bat signal :)
Jason Punyon
+1  A: 

Just adding my 2 cents) That's a Mono bug, here is the description.

n535