使派生类中的只读属性可写

时间:2010-02-11 10:46:52

标签: c#

我想要实现的目标如下: 我在BaseClass中声明了一个属性。如果通过基类'指针访问此属性,则只有getter可用,但如果使用派生类指针,我希望能够获取并设置该属性。所以intellisense甚至不应该为基指针显示一个setter。

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

一个现实的例子
考虑一种情况,即您拥有从Person类派生的Parent和Child类。想象一下属性-RestrictionLevel,两者都可以读取它,但只有父级可以设置值。有没有更好的方法来设计这种情况?

4 个答案:

答案 0 :(得分:9)

我能想到的唯一方法就是用新的方式遮蔽财产:

public class DerivedClass : BaseClass
{
    int intValue;

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

注意如何声明属性new而不是override。当然,这意味着MyProperty类型的DerivedClassMyProperty类型的BaseClass无关,它是一个全新的属性,恰好具有同名(因此将其隐藏在基类中)。

结果如下:

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 */

另外,正如@silky在评论中所述:

  

(虽然我建议你做,然后   parent,引用相同的变量   避免一个非常混乱的情况)但是   我真的不认为这会永远   适当的。

...您可能希望新属性访问基类中的属性(通过base.MyProperty,使用受保护的setter完成)。例如,请考虑以下内容:

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

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

那就是说,使用new时我总觉得有点脏(当我想到它时,我不确定我是否真的用过生产代码)。

<强>更新
鉴于您给出的示例场景(我以Parent能够设置RestrictionLevel的{​​{1}}的方式解释),它可以像这样解决:< / p>

Child

这意味着以下代码有效:

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

......但这个不是:

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

方法Child c = new Child(); c.SetRestrictionLevel(c, RestrictionLevel.Low); 可以从任何后代类型(包括SetRestrictionLevelInternal)中调用,但不能从外部调用类型本身。因此,您无法在Child实例上调用SetRestrictionLevelInternal。在上面的示例中,我们选择公开Parent方法,该方法又调用public方法。

答案 1 :(得分:5)

编程到接口,而不是实现:

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

使用示例:

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

您可以轻松地将一些私有修饰符用于具有不同合同的接口。这个解决方案很简单,IMO适合您的任务 如果你认为做一些你想隐藏/限制的行动很容易,那你就是对的。 任何可能的方式也是如此。

答案 2 :(得分:0)

我发现完成此操作的唯一方法是将另一层引入继承层次结构。一层覆盖了基础的只读属性,使其引用可以更改的变量,而下一层则覆盖了具有可更新该变量的读写属性的只读属性。

对于您发布的示例,您可以执行以下操作:

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

行为是您想要的:

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

答案 3 :(得分:-1)

这是不可能的(AFAIK)。无论如何,这会令人困惑。你想做什么特别的原因?