Как сделать пользовательский атрибут C#
Хочу научиться работать с атрибутами, но не могу понять как правильно их написать. Сейчас пытаюсь сделать атрибут который будет задавать максимальное и минимальное значение для свойства типа int (типа как System.ComponentModel.DataAnnotations.RangeAttribute).
Как мне получить свойство, которое использует данный атрибут в моем классе RangeAttribute и сравнить значение свойства с полями?
[AttributeUsage(System.AttributeTargets.Property)]
sealed class RangeAttribute : System.Attribute
{
private int _minimum;
private int _maximum;
public RangeAttribute(int minimum, int maximum)
{
_minimum = minimum;
_maximum = maximum;
}
public override bool Match(object obj)
{
//Что тут писать? Или может мне этот метод вообще не нужен?
}
}
Использовать буду так:
class Test
{
[Range(1, 10)]
public int Value { get; set; }
}
Ответы (1 шт):
Если делать так, как именно хотите вы:
[AttributeUsage(System.AttributeTargets.Property)]
sealed class RangeAttribute : System.Attribute
{
private int _minimum;
private int _maximum;
public RangeAttribute(int minimum, int maximum)
{
_minimum = minimum;
_maximum = maximum;
}
public bool InRange(int value) => value >= _minimum && value <= _maximum;
}
class Test
{
private int _value;
[RangeAttribute(1, 10)]
public int Value
{
get => _value;
set => _value = this
.GetType()
.GetProperty(nameof(Value))
.GetCustomAttributes(true)
.Cast<RangeAttribute>()
.Single()
.InRange(value) ? value : _value;
}
}
Проверяем:
Test test = new Test();
test.Value //0 логично, _value по-умолчанию 0
test.Value = 1;
test.Value //1 успешно записалось
test.Value = 15;
test.Value //1 успешно соблюдает диапазон и не даёт записать 15
test.Value = 5;
test.Value //5 дал записать 5
Главное учитывайте, что на атрибуты, как и вообще на рефлексию с кучей вызовов методов - идут дополнительные вычислительные расходы.
PS цепочки вызовов в set'тере не избежать, можно конечно уменьшить, засунув под ковёр в отдельный класс, но магии не случится и атрибут сам не вызовется.
Данный подход нужен только на мой взгляд не более чём в 3 случаях:
- Самообучение с экспериментами
- Разработка библиотек/фреймворков
- Тырпрайз со своими стандартами
Если применение редко и разово, то лучше отказаться от атрибутов и сделать проще, а где нужно - ссылаться на внешний IsValidValue:
class Test
{
private int _value;
private const int _min_val = 1;
private const int _max_val = 10;
private bool IsValidValue(int value) => value >= _min_val && value <= _max_val;
public int Value
{
get => _value;
set => _value = IsValidValue(value) ? value : _value;
}
}
Можно сделать альтернативу любыми вариантами, создать поле для класса, который будет валидировать, делегат или прямо функцию/предикат проверки записывать в экземпляр класса Test.