views:

329

answers:

4

I have a class in vb.net like

Public Class Customer
    Private _Name As String
    Public Overridable Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
        End Set
    End Property
End Class

and a class deriving from it

Public Class ProxyCustomer
    Inherits Customer
    Private _name As String
    Public Overrides WriteOnly Property Name() As String
        Set(ByVal value As String)
            _name = value
        End Set
    End Property
End Class

which gives me following error Public Overrides WriteOnly Property Name() As String' cannot override 'Public Overridable Property Name() As String' because they differ by 'ReadOnly' or 'WriteOnly'

but i have same construct in C#

public  class Customer 
{
    public virtual string FirstName { get; set; }     
}

public class CustomerProxy:Customer {

   public override string FirstName
   {
       set
       {
           base.FirstName = value;
       }
   }

}

which works, so the first thing is , is this consistent because 2 languages are behaving in a very inconsistent way.

secondly, when i do a reflection to get a property, so for example

Dim propInfo  = GetType(Customer).GetProperty("Name")

the propINfo.canRead property is always false, shouldn't this be true since base class implements the getter of the property ?

many thanks

+2  A: 

I'll deal with the 2nd part first. In your current vb.net code, the derived Name property replaces the original for the reflection lookup. So that reflection code only see the WriteOnly version of the property. Worse, you completely replace the backing store in your derived setter, so your getter is checking a completely different variable than the one you set.

As for the first question, when overriding properties in vb.net, you always need to override both the getter and the setter, even if you just replace it with an identical implementation:

The code should read like this:

Public Class ProxyCustomer
    Inherits Customer

    Public Overrides Property Name() As String
        Get
            Return MyBase.Name ''# Return the original parent property value
        End Get
        Set(ByVal value As String)
            MyBase.Name = value
        End Set
    End Property
End Class
Joel Coehoorn
but shouldn't reflection accurately reflect whether you can actually read from the property or not?
Lasse V. Karlsen
So basically you're saying that reflection can say "No, you cannot read from property X", when in fact you can? What happens if you use reflection to read from the property, will that fail as well? This just seems inconsistent to me. I understand that reflection is just looking at some metadata-table, but if the property can be read from, I just feel that reflection should accurately report that.
Lasse V. Karlsen
No, because the reflection query was only checking the new inherited property. It doesn't know anything about the parent. This case also helps illustrate why is the _right_ way to do it, because you _can't_ read from the property. You can see this if you first set a new value on the property. If you then read from it, you'll see you're _not_ really reading from that property but from somewhere else, because it doesn't show your changes.
Joel Coehoorn
Sorry, was working on a delete/replace comment edit when you posted your follow-up.
Joel Coehoorn
Let me post my answer, please comment on that, since I feel that there's something I'm seeing that you're not. I could be wrong though :)
Lasse V. Karlsen
Posted, and changed, to show that reflection-GetValue crashes, even though the property is readable from code.
Lasse V. Karlsen
Joel, thanks for the answer, the code in the question is simplified version, i am actually using castle dynamic proxy and seeing this behavior. I thought the overriding rules between vb and c3 are consistent. so whats the right way to check if you can read or not, keep going up the type tree ?
The right way is to override both parts of the property. Then reflection will return the correct "canRead" value.
Joel Coehoorn
A: 

The two (c# and vb) derived classes you posted are not equivalent. To try to get the same behavior (won't compile):

   public override string FirstName
   {
       protected get { return base.FirstName};
       set
       {
           base.FirstName = value;
       }
   }

You'll get "Override accessor .... cannot change access rights". What's actually happening is you are overriding the set accessor only.

Philip Rieck
A: 

Here is excerpt from 10.6.3 Virtual, sealed, override, and abstract accessors:

An overriding property declaration must specify the exact same accessibility modifiers, type, and name as the inherited property. If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property must include only that accessor. If the inherited property includes both accessors (i.e., if the inherited property is read-write), the overriding property can include either a single accessor or both accessors.

So C# acts as expected.

Dzmitry Huba
I'm not surprised by the allowance to only override one setter, although it isn't typically done. I'm, however, surprised that reflection doesn't allow or report what normal C# code can actually do.
Lasse V. Karlsen
+1  A: 

I'm a bit surprised by this, as it seems by the answer by Joel that this is in fact how it is supposed to work.

In my mind, however, it seems like this: "Property X can be read, even though you overrode it in a descendant class and only implemented the setter, but reflection says no, you can't read it".

This, to me, looks inconsistent. "Yes, you can read from the property using normal C# code, but no, reflection will not say that you can nor will it allow you to read from it".

Here's an example piece of code. The code reports:

Base.Value.CanRead: True
Base.Value.CanWrite: True
Derived.Value.CanRead: False
Derived.Value.CanWrite: True
Setting via Derived.Value
Derived.Value: Test
(reflection get crashes with method Get not found, so commented it out)
Setting via Derived.Value
Base.Value: Test2

Note that even when writing through the Value property on Base (when I have a Derived instance), it still goes through the overridden property.

and the code:

using System;
using System.Reflection;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            Type type = typeof(Base);
            PropertyInfo prop = type.GetProperty("Value");
            Console.Out.WriteLine("Base.Value.CanRead: " + prop.CanRead);
            Console.Out.WriteLine("Base.Value.CanWrite: " + prop.CanWrite);

            type = typeof(Derived);
            prop = type.GetProperty("Value");
            Console.Out.WriteLine("Derived.Value.CanRead: " + prop.CanRead);
            Console.Out.WriteLine("Derived.Value.CanWrite: " + prop.CanWrite);

            Derived d = new Derived();
            d.Value = "Test";
            Console.Out.WriteLine("Derived.Value: " + d.Value);
            // Console.Out.WriteLine("Reflected value: " + prop.GetValue(d, null));

            Base b = new Derived();
            b.Value = "Test2";

            Console.In.ReadLine();
        }
    }

    public class Base
    {
        public virtual String Value { get; set; }
    }

    public class Derived : Base
    {
        public override string Value
        {
            set
            {
                Console.Out.WriteLine("Setting via Derived.Value");
                base.Value = value;
            }
        }
    }
}
Lasse V. Karlsen
The key is the the derived type is a brand new type. And so the reflection call and the derived only returns data on the derived property. There might be a bug in there because of the name ambiguity, but the derived property is a brand new property and you need some way to query reflection on just that property.
Joel Coehoorn
But it has overridden the setter of base, when using a derived instance, so it's not just a new property. See my updated answer, note that when using "Base b = new Derived()" and then setting via Value, it will go through the overridden code.
Lasse V. Karlsen