繁体   English   中英

如何将 set 访问器添加到继承的只读属性

[英]How to add a set accessor to an inherited read-only property

我有一个关于在 C# 中覆盖属性的问题。 这里已经有一个类似的问题,但答案对我来说并不令人满意。 假设我有这些课程:

class A
{
    public int Prop { get; }
}

class B : A
{
    public int Prop { get; set; }
}

现在如您所见,我想在子类中的属性 Prop 中添加一个 setter。 我想出了两个解决方案。 第一个是使属性虚拟并在 B 类中覆盖它,如下所示:

class A
{
    public virtual int Prop { get; }
}

class B : A
{
    public override int Prop { get; set; }
}

但不幸的是,编译器不允许我这样做。 我的第二个想法是使用“新”关键字:

class A
{
    public virtual int Prop { get; }
}

class B : A
{
    public new int Prop { get; set; }
}

现在一切似乎都有效,但由于一个细节,这对我来说并不是一个令人满意的解决方案。 例如,让我们考虑这段代码:

B b = new B();
b.Prop = 5;
A a = b;
Console.WriteLine(a.Prop);

您可能知道我在输出中得到 0,但我想得到 5。有没有办法解决这个问题?

您可以尝试显式实现属性的设置器。 这需要一个接口:

public interface IPropSet
{
    int Prop { set; }
}

class A
{
    protected int prop;
    public int Prop => prop;
}

class B : A, IPropSet
{
    int IPropSet.Prop
    {
        set => prop = value;
    }
}

但不幸的是,如果不强制转换为IPropSet ,则无法设置此属性:

((IPropSet)new B()).Prop = 1;

另一种选择是使用支持字段并在B中将Prop创建为new的,如下所示:

class A
{
    protected int prop;
    public int Prop => prop;
}

class B : A
{
    public new int Prop
    {
        get => prop;
        set => prop = value;
    }
}

示例用法:

var b = new B { Prop = 1 };
Console.WriteLine(((A)b).Prop); // 1

class A中属性的set -ter 应该是protected 您仍然可以覆盖它,并且在class A及其子级之外仍然无法访问它。

编辑:我再次阅读了您的问题并在Visual Studio中测试了我编辑的答案(我第一次没有机会这样做)。

据我了解,除了覆盖属性之外,您还希望能够从类外部在子类上设置属性值,但不能在基类上设置它。

你可以尝试这样的事情:

using System;

namespace PropertyInheritance
{
    public class Program
    {
        static void Main(string[] args)
        {
            A a1 = new A();
            B b1 = new B();

            a1.Prop = 1;
            b1.Prop = 2;

            Console.WriteLine($"a1.Prop = {a1.Prop}");
            Console.WriteLine($"b1.Prop = {b1.Prop}");

            Console.WriteLine($"a1.GetPropField() = {a1.GetPropField()}");
            Console.WriteLine($"b1.GetPropField() = {b1.GetPropField()}");

            B b2 = new B();
            b2.Prop = 5;
            A a2 = b2;
            Console.WriteLine($"a2.Prop = {a2.Prop}");

            Console.WriteLine($"a2.GetPropField() = {a2.GetPropField()}");

            Console.ReadLine();
        }
    }

    public class A
    {
        protected int prop;

        public virtual int Prop {
            get { return prop; }
            set { }
        }

        // test method - to check field's value
        public int GetPropField() => prop;
    }

    public class B : A
    {
        public override int Prop
        {
            get { return prop; }
            set { prop = value; }
        }
    }
}

这是输出:

在此处输入图像描述

我想一切都清楚了,但如果没有,请随时询问。 也许我仍然没有理解这个问题,甚至在我的回答中犯了一些错误。

Console.WriteLine(a.Prop); 您引用 A.Prop,B 类中的新 Prop 用于重新定义。

当用作声明修饰符时,new 关键字显式隐藏从基类继承的成员。 当您隐藏继承的成员时,该成员的派生版本将替换基类版本。 这假定成员的基类版本是可见的,因为如果它被标记为私有或在某些情况下是内部的,它将已经被隐藏。 尽管您可以在不使用 new 修饰符的情况下隐藏公共或受保护成员,但您会收到编译器警告。 如果您使用 new 显式隐藏成员,它会抑制此警告。 微软文档

在此处输入图像描述

但你可以使用,

if (a is B bb)
{
    Console.WriteLine(bb.Prop);
}

或者使用这个模式

class A
{
    public virtual int Prop { get; protected set; }
}
class B : A
{
     public void SetProp(int prop) => Prop = prop;
}


B b = new B();
b.SetProp(5);
A a = b;
Console.WriteLine(a.Prop);

A 不是 B 的子类,这不是继承的工作方式。 我不明白你为什么要这样做,我认为你误解了继承的完整概念

如果您建议:

 class A
{
    public int Prop { get; }
}

class B : A
{
    public int Prop { get; set;  }
}

它们是相同的属性。 如果您执行以下操作:

 class A
{
    public int Prop { get; }
}

class B : A
{
    public int AnotherProp { get; set;  }
}

你可以设置:

B instance = new B();
B.Prop = 5;

如果您想要做的是在子类中拥有一个具有相同名称的属性(不推荐),您可以这样做,而无需使用继承:

internal class A
{
    internal int Prop { get; }
}

internal class B 
{
    internal A MySubClass { get; set;  }
}

B instanceB = new B();
A instanceA = new A();
instanceA.Prop = 5;
B.MySubClass = instanceA;

或者你可以玩它,而不直接复制它。 只是一个例子,向您展示class as propertyinheritance之间的区别

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM