Автосвойства и рекурсия

207
11 декабря 2018, 17:00

Есть вот такой синтаксический сахар, как авто-свойство, когда за кулисами при компиляции создается поле.

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

Ну например, хочу, что бы при присвоении значения меньше 0, оно заменялось на 0.

Answer 1

Нет, так сделать нельзя.

Согласно спецификации автосвойствами являются свойства у которых и в геттере и в сеттере тело состоит из одной точки с запятой, и, соответственно, нет логики. Неявное поле создается только для автосвойств:

An automatically implemented property (or auto-property for short), is a non-abstract non-extern property with semicolon-only accessor bodies. Auto-properties must have a get accessor and can optionally have a set accessor.
When a property is specified as an automatically implemented property, a hidden backing field is automatically available for the property, ...

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

Рекурсия в данном случае тоже не поможет, без поля эта рекурсия будет бесконечной:

int backingField;
public int BadProperty
{
    get { return backingField; }
    set
    {
        //с полем рекурсия бесссмыслена, но возможна
        if (value < 0) BadProperty = 0;
        else backingField = value;
    }
}
public int ImpossibleProperty
{
    set
    {
        //без поля непонятно что делать
        if (value < 0) ImpossibleProperty = 0;
        //куда присваивать значение???
    }
}

Альтернативы

хочу, что бы при присвоении значения меньше 0, оно заменялось на 0.

Можно смошенничать и формально добиться нужного поведения используя для свойства свою структуру и написать для нее свое приведение типов с проверкой значения. Например, так:

//структура
struct NonNegativeValue
{
    private readonly int value;
    private NonNegativeValue(int value)
    {
        this.value = Math.Max(value, 0);
    }
    public static implicit operator NonNegativeValue(int value)
    {
        return new NonNegativeValue(value);
    }
    public static implicit operator int(NonNegativeValue obj)
    {
        return obj.value;
    }
}
internal class MyClass
{   
    //свойство
    public NonNegativeValue Value { get; set; }
}

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

var obj = new MyClass();
Console.WriteLine(obj.Value); //0
obj.Value = -1;
Console.WriteLine(obj.Value); //0
obj.Value = 5;
Console.WriteLine(obj.Value); //5

Возможно есть другие варианты реализации: через обработку атрибутов, либо через генераторы кода. Сомневаюсь, впрочем, что какой-нибудь из этих вариант будет проще чем свойство с полем:

private int value;
public int Value
{
    get { return value; }
    set { this.value = value < 0 ? 0 : value; }
}
Answer 2

Как указали в комментариях, такое можно сделать с помощью сторонних AOP-фреймворков, например с помощью PostSharp

Здесь находится документация по написанию аспектов: ссылка

Подключите к проекту пакет PostSharp и напишите такой аспект:

[PSerializable]
class ValueCorrectorAttribute : LocationInterceptionAspect
{
    public override void OnSetValue(LocationInterceptionArgs args)
    {
        if ((int)args.Value < 0)
            args.Value = 0;
        args.ProceedSetValue();
    }
}

Используем:

class A
{
    [ValueCorrector]
    public int Prop { get; set; }
}

Тестируем:

var a = new A { Prop = 10 };
Console.WriteLine(a.Prop); // 10
a.Prop = -10;
Console.WriteLine(a.Prop); // 0

При желании можете добавить параметры к аспекту и получить чуть более универсальное решение.

READ ALSO
Ошибка сервера 500 после POST запроса через WebClient на C#

Ошибка сервера 500 после POST запроса через WebClient на C#

Вот так выглядит запросВ ответ на него сервер возвращает ошибку 500 в Visual Studio 2017 на Windows

143
Как работает геолокация в Uber или Yandex taxi?

Как работает геолокация в Uber или Yandex taxi?

Начал делать приложение и возник вопрос, а как правильно делать геолокациюЕсть несколько клиентов, каждый клиент должен получать геолокацию...

195
C# WinForms ComboBox SelectedValue null

C# WinForms ComboBox SelectedValue null

Когда закрываю форму легко получаю значение SalectedValue ComboBox но перед ShowDialog формы если ставлю SelectedValue для ComboBox, то ComboBox не ставит егоComboBox внутри...

398
Как корректно отформатировать таблицу Excel, используя C#

Как корректно отформатировать таблицу Excel, используя C#

Всем привет! Появилась задача из полученных данных перекинуть в таблицу и её отформатировать Есть такой вот код:

203