6

What I want to achieve is the following : I have a property declared in the BaseClass. If this property is accessed via the base class' pointer, only the getter should be available but if the derived class pointer is used I want to be able to get and set the property. So the intellisense should not even show a setter for the base pointer.

public class BaseClass
{
     public virtual int MyProperty
     {
         get { return 1; }
         set {;}//This would show the setter in Intellisense
     }
}   

public class DerivedClass : BaseClass
{
     int intValue;

     public override int MyProperty
     {
         set { intValue = value;}
     }
}   

A realistic example:
Consider a situation where you have a Parent and Child class derived from the Person class. Imagine a property -RestrictionLevel, where both can read it but only the parent can set the value. Is there a better way to design this situation?

alwayslearning
  • 4,493
  • 6
  • 35
  • 47
  • Cross-link: http://stackoverflow.com/questions/1489361/override-abstract-readonly-property-to-read-write-property – Mikhail Poda Apr 06 '11 at 10:47
  • Just needed to this to stop a readonly ref to a object being indirectly updated. Thanks for asking the quesions before I needed the answer. – Ian Ringrose Oct 06 '11 at 11:57

4 Answers4

9

The only way I could think of is to shadow the property with a new one:

public class DerivedClass : BaseClass
{
    int intValue;

    public new int MyProperty
    {
        get { return intValue; }
        set { intValue = value; }
    }
}  

Notice how the property is declared new instead of override. This means, of course, that MyProperty of the type DerivedClass has nothing to do with MyProperty of the type BaseClass, it's a brand new property that just happens to have the same name (thus hiding the one from the base class).

The result is this:

DerivedClass d = new DerivedClass();
d.MyProperty = 42;

BaseClass b = new DerivedClass();
b.MyProperty = 42;    /* compilation error: Property or indexer 
                                            'TheNamespace.BaseClass.MyProperty' 
                                            cannot be assigned to -- it is 
                                            read only */

Also, as @silky states in the comment:

(though I suggest you make it, and the parent, refer to the same variable to avoid a very confusing situation) but I really don't think this would ever be appropriate.

...you may want to have the new property access the one from the base class (through base.MyProperty, completed with a protected setter). Consider the following for instance:

DerivedClass d = new DerivedClass();
d.MyProperty = 42;

BaseClass b = d;
Console.WriteLine(b.MyProperty);  // prints 1

That said, I always feel a little dirty when using new (which, when I think about it, I am not sure that I have actually done in production code).

Update
Given the example scenario that you give (that I interpret in a way that a Parent is to be able to set the RestrictionLevel of a Child), it could be solved like this:

public enum RestrictionLevel
{
    Low,
    Medium,
    Grounded
}

public class Person
{
    public RestrictionLevel RestrictionLevel { get; private set; }
    protected static void SetRestrictionLevelInternal(Person person, RestrictionLevel restrictionLevel)
    {
        person.RestrictionLevel = restrictionLevel;
    }
}

public class Child : Person { }

public class Parent : Person
{
    public void SetRestrictionLevel(Child child, RestrictionLevel restrictionLevel)
    {
        SetRestrictionLevelInternal(child, restrictionLevel);
    }
}

This means that the following code is valid:

Child c = new Child();
Parent p = new Parent();
p.SetRestrictionLevel(c, RestrictionLevel.Grounded);

...but this one is not:

Child c = new Child();
c.SetRestrictionLevel(c, RestrictionLevel.Low);

The method SetRestrictionLevelInternal can be called from within any descendant type (including Child), but cannot be invoked from outside the type itself. So, you cannot invoke SetRestrictionLevelInternal on a Parent instance. In the above example, we choose to expose a public method, which in turn invokes the protected method.

Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • I'll upvote this for being a 'solution' (though I suggest you make it, and the parent, refer to the same variable to avoid a very confusing situation) but I really don't think this would ever be appropriate. – Noon Silk Feb 11 '10 at 11:06
5

Program to an interface, not an implementation :

public interface IFoo
{
    int MyProp { get; }
}

public interface IFooWithSetter
{
    int MyProp { get; set; }
}

public class FooImplementation : IFoo, IFooWithSetter
{
    public int MyProp
    {
        get { return 100; }
        set { }
    }
}  

Usage sample:

IFoo var1 = new FooImplementation(); // only getter is available in var1
IFooWithSetter var2 = (IFooWithSetter) var1; // setter and getter is available in var2  

You can easily use some private modifiers for interfaces with different contracts. This solution is simple and IMO it's suites well for your task.
And if you think that it'll be easy to do some actions you are trying to hide/restrict, you are right. And the same is with any possible way.

zihotki
  • 5,201
  • 22
  • 26
0

The only way I've found to accomplish this is to introduce another layer to the inheritance hierarchy. One layer overrides the underlying read-only property to make it refer to a variable which can change, and the next layer hides the read-only property with a read-write property that can update that variable.

For your posted example, here's what you could do:

public class BaseClass
{
     public virtual int MyProperty => 1;
}   

public abstract class Intermediate : BaseClass
{
    private protected int? _myProperty;

    // overrides
    public sealed override int MyProperty => this._myProperty ?? base.MyProperty;
}

public class DerivedClass : Intermediate
{
     // hides
     public new int MyProperty 
     {
         get => base.MyProperty;
         set => base._myProperty = value;
     }
}

The behavior is what you'd want:

DerivedClass d = new DerivedClass();
Console.WriteLine(d.MyProperty); // 1
d.MyProperty = 2;
Console.WriteLine(d.MyProperty); // 2

BaseClass b = d; // view as BaseClass
Console.WriteLine(b.MyProperty); // 2
// b.MyProperty = 3; // compiler error
ChaseMedallion
  • 20,860
  • 17
  • 88
  • 152
-1

It's not possible (AFAIK). It would be confusing anyway. Any particular reason you want to do this?

Noon Silk
  • 54,084
  • 6
  • 88
  • 105
  • @silky Well, I want to avoid client code setting it on a base pointer but only on the derived pointer. Providing an empty setter would achieve the purpose but I would prefer providing a compile time error if an attempt is made to set it on the base pointer – alwayslearning Feb 11 '10 at 10:57
  • @alwayslearning: I just wonder what sort of structure would suggest such a requirement. It doesn't seem natural; but I could be mistaken. Can you elaborate on the exact case you are talking about? – Noon Silk Feb 11 '10 at 10:59