Абстрактное свойство с общедоступным геттером, возможно ли определить частный сеттер в конкретном классе?

Я пытаюсь создать абстрактный класс, который определяет свойство с помощью геттера. Я хочу оставить это на усмотрение производных классов, чтобы решить, хотят ли они реализовать установщик для свойства или нет. Это возможно?

Что у меня есть на данный момент:

public abstract class AbstractClass {
    public abstract string Value { get; }
    public void DoSomething() {
        Console.WriteLine(Value);
    }
}

public class ConcreteClass1 : AbstractClass {
    public override string Value { get; set; }
}

public class ConcreteClass2 : AbstractClass {
    private string _value;
    public override string Value {
        get { return _value; }
    }
    public string Value {
        set { _value = value; }
    }
}

public class ConcreteClass3 : AbstractClass {
    private string _value;
    public override string Value {
        get { return _value; }
    }
    public void set_Value(string value) {
        _value = value;
    }
}

В ConcreteClass1 я получаю сообщение об ошибке set. Он не может переопределить set_Value, потому что в AbstractClass не существует переопределяемого средства доступа к множеству.

В ConcreteClass2 я получаю сообщение об ошибке на обоих Value, потому что член с таким же именем уже объявлен.

ConcreteClass3 не выдает ошибки, но даже несмотря на то, что метод доступа Set Value будет скомпилирован в set_Value, обратного не будет. Определение set_Value не означает, что Value получает набор доступа. Поэтому я не могу присвоить значение свойству ConcreteClass3.Value. Я могу использовать ConcreteClass3.set_Value ("значение"), но это не то, чего я пытаюсь достичь здесь.

Возможно ли, чтобы абстрактный класс требовал общедоступного получателя, позволяя при этом определять необязательный сеттер в производном классе?

Если вам интересно, это всего лишь теоретический вопрос. У меня нет реальной ситуации, когда нужно что-то подобное. Но я могу представить себе абстрактный класс, которому все равно, как устанавливается свойство, но который должен иметь возможность получить свойство.


person comecme    schedule 10.01.2011    source источник


Ответы (4)


К сожалению, вы не можете делать именно то, что хотите. Вы можете сделать это с помощью интерфейсов:

public interface IInterface {
    string MyProperty { get; }
}

public class Class : IInterface {
    public string MyProperty { get; set; }
}

Я бы сделал это с помощью отдельного метода SetProperty в конкретных классах:

public abstract class AbstractClass {
    public abstract string Value { get; }
}

public class ConcreteClass : AbstractClass {

    private string m_Value;
    public override string Value {
        get { return m_Value; }
    }

    public void SetValue(string value) {
        m_Value = value;
    }
}
person thecoop    schedule 10.01.2011
comment
Используя interface, я не могу реализовать метод DoSomething в моем базовом классе (нет базового класса, только интерфейс). Использование SetValue такое же, как set_Value в моем ConcreteClass3. Но если это так, называть его SetValue, вероятно, лучше, чем set_Value. - person comecme; 10.01.2011
comment
Причина, по которой set_Value не связана со свойством, заключается в явном PropertyDef определении в сборке, которое связывает методы get_ и set_; простое наименование метода set_ не связывает его со свойством. В C # нет способа делать именно то, что вы хотите; вам придется пойти на компромисс. - person thecoop; 10.01.2011
comment
Вы можете реализовать DoSomething как метод расширения на IInterface. Смотрите мою правку. - person piedar; 12.07.2013
comment
Происходит ли это различие в способе работы производных свойств между абстрактными классами и интерфейсами из Спецификации общего языка или оно зависит от конкретной реализации? - person Fredrick; 15.08.2013
comment
Несмотря на большое количество голосов, это решение совершенно бесполезно, если единственная причина, по которой вам нужен set, - заставить его работать с сеткой свойств, которая не понимает методы. - person Stelios Adamantidis; 02.06.2021

Нашел решение: Как переопределить свойство только для получения с помощью сеттера в C #?

public abstract class A
{
    public abstract int X { get; }
}
public class B : A
{
    public override int X { get { return 0; } }
}
/*public class C : B  //won't compile: can't override with setter
{
    private int _x;
    public override int X { get { return _x; } set { _x = value; } }
}*/
public abstract class C : B  //abstract intermediate layer
{
    public sealed override int X { get { return this.XGetter; }  }
    protected abstract int XGetter { get; }
}
public class D : C  //does same thing, but will compile
{
    private int _x;
    protected sealed override int XGetter { get { return this.X; } }
    public new virtual int X { get { return this._x; } set { this._x = value; } }
}

D теперь эквивалентен классу, наследующему от B, а также имеет возможность переопределения в установщике.

person Nat    schedule 06.03.2014
comment
Хотя кажется, что технически это работает, это выглядит немного сложнее по сравнению с простотой принятого ответа. Не могу себе представить, чтобы убедить мою команду использовать его только для того, чтобы настроить некоторые свойства для целей тестирования :) Но все равно проголосуйте за это усилие. - person Sevenate; 18.11.2020

Вместо этого вы можете просто использовать модификатор доступа protected. Из-за наследования вам не разрешено использовать private. Выглядит это так:

public abstract class A
{
    public abstract int prop { get; protected set; }
}

public abstract class B : A
{
    public override int prop { get; protected set; }
}
person Владик    schedule 08.09.2020
comment
Я думаю, вы упустили половину ответа, где класс может установить значение prop. - person Kristianne Nerona; 09.09.2020
comment
Как производный класс (B) сможет реализовать общедоступный установщик для prop? - person comecme; 11.09.2020
comment
@comecme, оставь это как public int prop { get; set; } - person Владик; 14.09.2020
comment
Если класс A имеет {get; protected set;}, а я создаю класс B с public override int prop {get; protected set;}, он не компилируется. Я не могу переопределить свойство с защищенным сеттером с помощью общедоступного сеттера. - person comecme; 24.09.2020

Не очень элегантно, но это самое близкое, что вы можете получить, не делая чего-то похожего на конкретный класс3.

public class Concrete : AbstractClass
{
    public new void DoSomething()
    {
        Console.WriteLine(Value);
    }
}

public abstract class AbstractClass
{
    protected AbstractClass()
    {
        try
        {
            var value = Value;
        }
        catch (NotImplementedException)
        {
            throw new Exception("Value's getter must be overriden in base class");
        }
    }
    public void DoSomething()
    {
        Console.WriteLine(Value);
    }

    /// <summary>
    /// Must be override in subclass
    /// </summary>
    public string Value { get { throw new NotImplementedException(); } }
}
person Rob    schedule 10.01.2011
comment
Я не понимаю, почему вы определяете new DoSomething в конкретном классе. Кроме того, ваше решение вызовет исключение во время выполнения, во время компиляции отсутствие реализации Value вообще не будет замечено. - person comecme; 10.01.2011