C# 使派生类中的属性为只读

C# 使派生类中的属性为只读,c#,properties,overriding,access-modifiers,C#,Properties,Overriding,Access Modifiers,我正在重写我的派生类中的一个属性,我想使它成为只读的。C#编译器不允许我更改访问修饰符,因此它必须保持公共 最好的方法是什么?我是否应该在set{}中抛出invalidoOperationException?让setter在派生类中抛出invalidoOperationException违反了规则。本质上使setter的使用与基类的类型相关,这基本上消除了多态性的值 派生类必须遵守其基类的约定。如果setter在所有情况下都不合适,那么它就不属于基类 解决这个问题的一种方法是稍微分解层次结构 c

我正在重写我的派生类中的一个属性,我想使它成为只读的。C#编译器不允许我更改访问修饰符,因此它必须保持公共


最好的方法是什么?我是否应该在
set{}
中抛出
invalidoOperationException

让setter在派生类中抛出
invalidoOperationException
违反了规则。本质上使setter的使用与基类的类型相关,这基本上消除了多态性的值

派生类必须遵守其基类的约定。如果setter在所有情况下都不合适,那么它就不属于基类

解决这个问题的一种方法是稍微分解层次结构

class C1 { 
  public virtual int ReadOnlyProperty { get; } 
}
class C2 { 
  public sealed override int ReadOnlyProperty { 
    get { return Property; }
  }
  public int Property {
    get { ... }
    set { ... }
  }
}

在此场景中,您遇到问题的类型可以继承
C1
,其余类型可以切换为从
C2

派生。您可以隐藏原始实现并返回基本实现:

class Foo
{
    private string _someString;
    public virtual string SomeString 
    {
        get { return _someString; }
        set { _someString = value; }
    }
}

class Bar : Foo
{
    public new string SomeString 
    { 
        get { return base.SomeString; }
        private set { base.SomeString = value; }
    }
}

namespace ConsoleApplication3
{
    class Program
    {
        static void Main( string[] args )
        {
            Foo f = new Foo();
            f.SomeString = "whatever";  // works
            Bar b = new Bar();
            b.SomeString = "Whatever";  // error
        }
    }
}

然而,。正如Jared提到的,这是一种奇怪的情况。为什么不首先在基类中将setter设置为private或protected?

不允许在C#中隐藏基类的公共成员,因为有人可以获取派生类的实例,将其填充到基类的引用中,并以这种方式访问属性


如果您想使一个类提供另一个类的大部分(但不是全部)接口,那么必须使用聚合。不要从“基类”继承,只需在“派生”中逐个值包含一个,并为希望公开的“基类”功能部分提供自己的成员函数。然后,这些函数可以将调用转发到相应的“基类”函数。

只需使用new关键字定义属性,并且不在派生类中提供Setter方法。这也将隐藏基类属性,但是正如mjfgates提到的那样,仍然可以通过将基类属性分配给基类实例来访问基类属性。这就是多态性。

在某些情况下,您描述的模式是合理的。例如,拥有一个抽象类
MaybeMutableFoo
,从中派生子类型
MutableFoo
ImmutableFoo
,可能会有所帮助。这三个类都包括一个
IsMutable
属性和方法
AsMutable()
AsNewMutable()
,以及
AsImmutable()

在这种情况下,
MaybeMutableFoo
公开读写属性是完全合适的,前提是它的契约明确规定除非
IsMutable
返回true,否则setter可能无法工作。具有类型为
的字段的对象可能是可变foo
,而该字段恰好包含
ImmutableFoo
的实例,除非或直到它必须写入该实例,否则该对象可能会非常满意该实例,因此它将使用通过
AsMutable()
返回的对象替换该字段,然后将其用作可变foo(它会知道它是可变的,因为它刚刚替换了它)。拥有
MaybeMutableFoo
include一个setter可以避免在引用可变实例后在字段上进行任何未来的类型转换

允许这种模式的最佳方式是避免实现虚拟或抽象属性,而是实现其getter和setter链接到虚拟或抽象方法的非虚拟属性

public class MaybeMutableFoo
{
    public string Foo {get {return Foo_get();} set {Foo_set(value);}
    protected abstract string Foo_get();
    protected abstract void Foo_set(string value};
}
然后派生类
ImmutableFoo
可以声明:

    new public string Foo {get {return Foo_get();}

使其
Foo
属性为只读,而不干扰为抽象
Foo\u get()和
Foo\u set()编写重写的能力
方法。请注意,
Foo
的只读替换不会更改属性get的行为;只读版本链接到与基类属性相同的基类方法。这样做将确保有一个用于更改属性getter的修补点,一个用于更改setter的修补点,以及即使属性本身被重新定义,这些补丁点也不会改变。

我认为在这种情况下,更好的解决方案是新关键字

public class Aclass {        
    public int ReadOnly { get; set; }
}

public class Bclass : Aclass {        
    public new int ReadOnly { get; private set; }
}

我想我们需要更多的上下文。除了如果你在你的例子中使用“Foo f2=b;f.SomeString=“eeba geeba!”;编译器允许你使用。@mjfgates:我猜你的意思是
Foo f=new Bar();f.SomeString=“blah”
?这就是我没有走这条路线的原因。我无法更改基类,因为我是从标准ASP.NET web控件派生的。不,我指的是我编写的。在这种情况下,您调用Foo.SomeString是因为SomeString在Bar中不是多态的,它隐藏了Foo实现。@Ed:我指的是mjfgates编写的内容(尽管间接地是关于你写的东西)。无论如何,我理解这是如何工作的,不希望两种实现根据您使用的类型而工作不同。抱歉,我错过了在手机上的响应。我100%同意这不是最佳的。但问题来自最初的请求。如果您做出了错误的设计选择,您可能会做出更糟糕的设计c我不会试图隐藏setter并避免破坏接口,因此如果基类有一个
IsReadOnly
属性并抛出自己的异常,那么它就可以了?这就是
NameObjectCollectionBase
NameValueCollection
中的
HttpValueCollection
的方式ritance有效,被
页面的
请求使用。Param
。我理解该原则背后的原因,但在
NameObjectCollectionBase
的情况下,它与您的陈述相矛盾:
如果setter在所有情况下都不合适,那么它就不属于基类。
不幸的是,我是使用.NETC