覆盖抽象只读属性为可读/写属性

31

我希望只在一个基础抽象类中强制实现C#属性的getter。派生类可以选择提供该属性的setter以便公开使用静态绑定的类型。

给定以下抽象类:

public abstract class Base
{
    public abstract int Property { get; }
}

如果我想要一个派生类同时实现setter,那么我可以尝试这样做:

public class Derived : Base
{
    public override int Property
    {
        get { return field; }
        set { field = value; } // Error : Nothing to override.
    } 

    private int field;
}

由于我试图覆盖不存在的setter,所以出现了语法错误。我尝试了一些其他方式,例如将基础setter声明为私有等,但仍然遇到各种错误,阻止我这样做。必须有一种方法可以做到这一点,因为它不会违反任何基类协定。

顺便说一下,使用接口可以实现,但我真的需要默认实现。

我经常遇到这种情况,我想知道是否有一个隐藏的C#语法技巧可以解决这个问题,否则我就只能自己实现一个手动的SetProperty()方法。


交叉链接:https://dev59.com/ekvSa4cB1Zd3GeqPiOl1 - Mikhail Poda
5个回答

20

你无法直接这样做,因为你不能在同一类型上使用相同签名的newoverride;有两个选项 - 如果你控制基类,则添加一个第二个属性:

public abstract class Base
{
    public int Property { get { return PropertyImpl; } }
    protected abstract int PropertyImpl {get;}
}
public class Derived : Base
{
    public new int Property {get;set;}
    protected override int PropertyImpl
    {
        get { return Property; }
    }
}

否则,您可以在类层次结构中引入一个额外级别:

public abstract class Base
{
    public abstract int Property { get; }
}
public abstract class SecondBase : Base
{
    public sealed override int Property
    {
        get { return PropertyImpl; }
    }
    protected abstract int PropertyImpl { get; }
}
public class Derived : SecondBase
{
    public new int Property { get; set; }

    protected override int PropertyImpl
    {
        get { return Property; }
    }
}

你的解决方案很有趣,我会仔细思考一下。 - Coincoin
1
你的解决方案是最接近事物的。但我认为不可能在不迫使派生类做杂技的情况下完成它。 - Coincoin

7
这符合您的需求吗?
public abstract class TheBase
{
    public int Value
    {
        get;
        protected set;
    }
}
public class TheDerived : TheBase
{
    public new int Value
    {
        get { return base.Value; }
        set { base.Value = value; }
    }
}
virtual 已经被移除,但基本值仍是唯一的存储值。所以这应该显示“5”。编译器应该会抱怨 b.Value = 4;
TheDerived d = new TheDerived();
d.Value = 5;
TheBase b = d;
//b.Value = 4;    // uncomment for compiler error
cout << "b.Value == " << b.Value << endl;

-杰西


3
怎么样像这样的东西:
public abstract class Base
{
    public virtual int Property
    {
        get { return this.GetProperty(); }
        set { }
    }

    protected abstract int GetProperty();
}

1
任何继承者现在都必须覆盖它们两个 - 有点冗余? - Marc Gravell
只有在覆盖属性时才需要使用。否则,没有虚拟属性也可以是可接受的替代方案。 - Coincoin
1
如果您不覆盖该属性,则 set 的目的非常... 不寻常。 - Marc Gravell
您是正确的,我完全误读了答案。它必须在某个地方新建。 - Coincoin
据我所知,它可以满足要求,唯一的注意事项是添加setter需要同时添加一个getter,该getter只调用基类。 - Rex M
这是一种平局,但我选择了另一个答案,因为我对基类中的空设置器感到不安。然而,我认为没有完美的答案。 - Coincoin

1
我有一个类似的需求,需要一个接口能够在两个松散相关的类之间共享常见的排序功能。其中一个类有一个只读Order属性,另一个类有一个可读写的Order属性,但我需要一种方式从两个类中以相同的方式读取属性。
事实证明,这可以通过在派生接口中隐藏只读值来实现。以下是我的做法。
interface ISortable
{
    int Order { get; }
}

interface ISortableClass2
    : ISortable
{
    // This hides the read-only member of ISortable but still satisfies the contract
    new int Order { get; set; }
}

class SortableClass1
    : ISortable
{
    private readonly int order;

    public SortableClass1(int order)
    {
        this.order = order;
    }

    #region ISortable Members

    public int Order
    {
        get { return this.order; }
    }

    #endregion
}

class SortableClass2
    : ISortableClass2
{
    #region ISortableClass2 Members

        public int Order { get; set; } 

    #endregion
}

class RunSorting
{
    public static void Run()
    {
        // Test SortableClass1
        var list1 = new List<SortableClass1>();

        list1.Add(new SortableClass1(6));
        list1.Add(new SortableClass1(1));
        list1.Add(new SortableClass1(5));
        list1.Add(new SortableClass1(2));
        list1.Add(new SortableClass1(4));
        list1.Add(new SortableClass1(3));

        var sorted1 = SortObjects(list1);

        foreach (var item in sorted1)
        {
            Console.WriteLine("SortableClass1 order " + item.Order);
        }

        // Test SortableClass2
        var list2 = new List<SortableClass2>();

        list2.Add(new SortableClass2() { Order = 6 });
        list2.Add(new SortableClass2() { Order = 2 });
        list2.Add(new SortableClass2() { Order = 5 });
        list2.Add(new SortableClass2() { Order = 1 });
        list2.Add(new SortableClass2() { Order = 4 });
        list2.Add(new SortableClass2() { Order = 3 });

        var sorted2 = SortObjects(list2);

        foreach (var item in sorted2)
        {
            Console.WriteLine("SortableClass2 order " + item.Order);
        }
    }

    private static IEnumerable<T> SortObjects<T>(IList<T> objectsToSort) where T : ISortable
    {
        if (objectsToSort.Any(x => x.Order != 0))
        {
            return objectsToSort.OrderBy(x => x.Order);
        }

        return objectsToSort;
    }
}

-1
您可以通过以下构造函数来实现这个功能;
public abstract class Base
{
    public abstract int Property { get; }
}


public class Derived : Base
{
    public Derived(string Property) : base(Property)
    {

    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接