继承自动实现的属性,派生类中只需要 getter

本文关键字:getter 派生 实现 属性 继承 | 更新日期: 2023-09-27 18:33:46

我有下一个代码

class Base
{
    public virtual int Prop { get; set; }
}
class Derived : Base
{
    public override int Prop { get { return 1; } }
}
//...
Derived obj = new Derived();
int some = obj.Prop; //expected
obj.Prop = 10; //oops it works

乍一看,最后一行应该符合的事实似乎并不那么明显。在我的程序中,我遇到一种情况,即以这种方式覆盖某些自动实现的属性将是一种解决方案。我知道这不是一个好方法。我可以做什么样的重构来避免这种继承并清理我的代码?谢谢

继承自动实现的属性,派生类中只需要 getter

生类必须实现与其基类相同的接口 - 如果从派生类无法访问公共资源库会破坏多态性。

如果客户端需要无法访问 Prop,但您需要能够从类本身中设置其值,则可以将其声明为:

public virtual int Prop { get; protected set; }

这个问题可能没有一个单一的答案,因为它取决于您特定应用程序的模型。如果某些派生类需要允许写入此属性,而其他派生类不需要,则可以在无效写入时引发异常并在运行时处理它,或者使用受保护的支持字段和仅 getter 实现该属性,然后添加一个派生类,该派生类为需要它的类提供 SetProp() 方法。

public class Base
{
    protected int prop;
    public virtual int Prop { get { return prop; } }
}
public class WriteableBase : Base
{
    public virtual void SetProp(int prop) { this.prop = prop; }
}
class Base
{
    public virtual int Prop { get; set; }
}
class Derived : Base
{
    public new int Prop { get { return 1; } private set {}  }
}

问题是,如果将派生转换为 Base,则无论如何都可以设置该属性。如果属性中继字段,它将被覆盖。

前任。:

class Base
{
    protected int fProp;
    public virtual int Prop { get { return fProp; } set { fProp = value; } }
}
class Derived : Base
{
    public Derived()
    {
        fProp = 1;
    }
    public new int Prop { get { return fProp; } private set {}  }
}

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            //...
            Derived obj = new Derived();
            int some = obj.Prop; //expected
            Base b = (Base)obj;
            b.Prop = 10; //oops it works
            Console.WriteLine(obj.Prop); =>it will show 10, not 1
            Console.ReadKey();
        }
    }
}
避免

此类问题的"更好"方法是,如果要"更改"派生类上的某些内容,请避免使用基类。或者,仅放置必须由所有派生类实现的最小内容,并让派生类实现只有它们需要的任何额外代码。

前任:

class Base
{
    protected int fProp;
}
class Derived : Base
{
    public Derived()
    {
        fProp = 1;
    }
    public int Prop { get { return fProp; } }
}
class Derived2 : Base
{
    public int Prop { get { return fProp; }  set { fProp = value; } }
}

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            //...
            Derived obj = new Derived();
            int some = obj.Prop; //expected
            Base b = (Base)obj;
            //obj.Prop = 10; Compilation error
            Console.WriteLine(obj.Prop);
            Derived2 obj2 = new Derived2();
            obj2.Prop = 10;
            Console.WriteLine(obj2.Prop);
            Console.ReadKey();
        }
    }
}

此外,您可以"封装"基类:

class Derived
{
    protected Base fBase;
    public Derived()
    {
        fBase = new Base;
    }
    //implement enything that you need to access from Base class
    public int Prop { get { return 1; } }
}

但我发现最后一个太"贵"了......:)

我认为在这种情况下不可能出现编译器错误。进一步想象一下,你会声明obj不是Derived而是Base = new Derived(),编译器应该如何知道要推断哪个属性。因此,您所能做的就是在运行时在派生资源库中引发异常,告知不允许设置此属性此类型。

class Base
{
    public virtual int Prop { get; protected set; }
}
class Derived : Base
{
    public override int Prop { 
        get { return 1; } 
        protected set {throw NotSupportedException();}  
    }
}

编译时,C# 将 getter 和 setter 转换为单独的方法(get_Prop 和 set_Prop)。代码仅实现 Derived 类中的getset仍然是基类的。

如果这是你想要的行为,我不认为它是错的。

如果你试图在派生类中隐藏资源库,没有优雅的方法可以做到这一点,所以抛出一个 NotSupportedException 是一种解决方案。

    class Base
    {
        public virtual int Prop { get; set; }
    }
    class Derived : Base
    {
        public override int Prop { get { return 1; } set { throw new NotSupportedException();}}
    }