JavaRush /Курсы /C# SELF /Extension Members: свойства

Extension Members: свойства

C# SELF
18 уровень , 3 лекция
Открыта

1. Введение

Раньше можно было добавлять классам только Extension-методы. А вот добавить свойство (property) как расширение класса было нельзя! То есть, если вы хотите, чтобы у любого DateTime появилось свойство IsWeekend, приходилось делать это через метод, что не всегда удобно — особенно если хочется синтаксис date.IsWeekend вместо date.IsWeekend().

С выходом C# 14 мечта сбылась: теперь можно писать свойства-расширения немного похожим на методы способом. Они позволяют добавить новое свойство к существующему типу, не изменяя его исходного кода, и использовать их как будто это обычные свойства!

Где это реально полезно?

  • Для стандартных типов .NET и сторонних библиотек, код которых изменить нельзя.
  • Для удобных «виртуальных» свойств в view-моделях, UI, вычисляемых представлениях.
  • Там, где хочется красивого синтаксиса вида .SomeCalculatedProperty без лишних скобок.

2. Новый синтаксис в C# 14

В C# 14 решили отказаться от ключевого слова this для расширений. Вместо этого ввели специальный оператор extension(Type this). И уже с его помощью можно добавлять в класс новые виртуальные сущности.

Старый синтаксис

public static class StringExtensions
{
    public static bool IsNullOrWhiteSpace(this string str)
        => string.IsNullOrWhiteSpace(str);
}

Новый синтаксис


public static class Enumerable
{
    extension(TSource source)
    {
        // Виртуальные члены для объекта
    }

    extension(TSource)
    {
        // Виртуальные статические члены
    }
}
Новый синтаксис extension members (C# 14+)

Важно! Этот новый extension-синтаксис работает только в C# 14+ и .NET 10+. Если вы пользуетесь .NET 9, то он работать не будет. Но если вы все же хотите его опробовать, а .NET 10 еще не вышел, то установите себе версию .NET 10 preview 7+
Примечание! Наш курс по C# использует самые новейшие возможности языка C# 14+, даже те, которые ещё официально не вышли. Осенью 2025 года выйдет .NET 10, и когда вы закончите курс, вы будете обладать самыми передовыми знаниями по C#! 😎

3. Синтаксис свойств-расширений

Как это выглядит?

Допустим, вы хотите добавить к типу DateTime свойство, которое будет возвращать, является ли дата выходным днём:

public static class DateTimeExtensions
{
    public static bool IsWeekend(this DateTime date) => 
        date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
}

Но это — старый вариант Extention Method, и вызывается как: myDate.IsWeekend()

Новый стиль — Extension Property


public static class DateTimeExtensions
{
    extension(DateTime source)
    {
        public bool IsWeekend // свойство-расширение!
        {
            get => source.DayOfWeek == DayOfWeek.Saturday || source.DayOfWeek == DayOfWeek.Sunday;
        }        
    }
}

Теперь вы можете писать так:

DateTime today = DateTime.Now;
if (today.IsWeekend)
{
    Console.WriteLine("Можно спать дольше!");
}

4. Какие бывают extension properties

Можно добавить не только read-only свойства, но и read-write:


public static class DateTimeExtensions
{
    extension(DateTime source)
    {
        public int YearFrom1900 // свойство-расширение!
        {
            get => source.Year - 1900;
            set => source.Year = value + 1900; 
        }        
    }
}
Read-write extension property
Важно: Для read-write extension properties вы должны понимать, что они изменяют сам объект, но не могут хранить "новое" значение где-то отдельно (как автоматическое поле). Если хотите добавить свойство, физически отсутствующее в объекте, потребуется отдельный сторедж (например, словарь), что выходит за рамки базового использования.

5. Статические свойства

Благодаря новому синтаксису, виртуальные свойства можно добавлять не только объекту, но и классу! Да, вы все правильно поняли, теперь мы можем добавлять виртуальные статические свойства к классу. Синтаксис почти такой же:


public static class DateTimeExtensions
{
    // Новый синтаксис: extension block для DateTime (C# 14)
    extension(DateTime)
    {
        // Статическое свойство для хранения текущей временной зоны пользователя
        private static TimeZoneInfo _currentTimeZone = TimeZoneInfo.Local;

        // Свойство-расширение для доступа к текущей временной зоне
        public static TimeZoneInfo CurrentTimeZone
        {
            get => _currentTimeZone;
            set => _currentTimeZone = value ?? TimeZoneInfo.Local;
        }

        // Возвращает время в пользовательской временной зоне
        public DateTime InCurrentTimeZone
        {
            get => TimeZoneInfo.ConvertTime(this, CurrentTimeZone);
        }
    }
}

6. Примеры: расширим класс Dog!

Давайте продолжим развивать наше приложение с собаками, которое мы бережно тянем из лекции в лекцию.

Допустим, теперь у нас есть не только собаки, но и список их прививок (List<DateTime>), но класс Dog из сторонней библиотеки, и мы не можем менять его код. Мы хотим добавить свойство: Сделаны ли все необходимые прививки в этом году?

Старый способ (метод-расширение):

public static class DogExtensions
{
    public static bool AllVaccinatedThisYear(this Dog dog)
    {
        return dog.Vaccinations.Any(v => v.Year == DateTime.Now.Year);
    }
}
// Использование:
if (myDog.AllVaccinatedThisYear())
    Console.WriteLine("Собака здорова!");

Новый способ (свойство-расширение):


public static class DogExtensions
{
    extension(Dog)
    {
        public bool AllVaccinatedThisYear
        {
            get => Vaccinations.Any(v => v.Year == DateTime.Now.Year);
        }
    }
}

// Использование:
if (myDog.AllVaccinatedThisYear)
    Console.WriteLine("Собака здорова!");

Полный пример

// Класс Dog — например, из сторонней библиотеки:
public class Dog
{
    public string Name { get; set; }
    public List<DateTime> Vaccinations { get; set; } = new List<DateTime>();
}

// Класс расширений с extension block:
public static class DogExtensions
{
    extension(Dog)
    {
        public bool AllVaccinatedThisYear
        {
            get => Vaccinations.Any(v => v.Year == DateTime.Now.Year);
        }
    }
}

// В программе:
var myDog = new Dog { Name = "Дружок" };
myDog.Vaccinations.Add(new DateTime(DateTime.Now.Year, 2, 16)); // прививка в этом году

Console.WriteLine($"{myDog.Name}: вакцинирована? {(myDog.AllVaccinatedThisYear ? "Да" : "Нет")}");

7. Таблица: Методы-расширения vs Свойства-расширения

Метод-расширение Свойство-расширение
Синтаксис вызова obj.Method() obj.Property
Передаёт параметры Да Нет (только this obj)
Можно записывать Не применимо Только если реализовано
Удобно для View/UI Иногда Да (двусторонние биндинги, лямбда)
Видимость в рефлексии Нет Нет

8. Потенциальные ловушки и типичные ошибки

Когда extension-свойства не помогут

  • Они не могут заменять полностью настоящие поля: у них нет доступа к приватным членам объекта.
  • Нельзя добавить автоматическое изменение состояния объекта (например, не получится учитывать изменение значения в исходном классе напрямую).
  • Нельзя использовать extension property для реализации интерфейса или абстрактного класса.

Ошибка с именами

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

Проблемы с сериализацией/рефлексией

Extension property — это не "реальный" член типа, а просто удобный синтаксический сахар. Поэтому, если где-то используется рефлексия или сериализация, свойство-расширение может быть не видно для таких инструментов.

2
Задача
C# SELF, 18 уровень, 3 лекция
Недоступна
Создание свойства-расширения для проверки високосного года
Создание свойства-расширения для проверки високосного года
2
Задача
C# SELF, 18 уровень, 3 лекция
Недоступна
Создание свойства-расширения для строки
Создание свойства-расширения для строки
Комментарии (5)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Evgeny Aseev Уровень 19
9 марта 2026
extension(DateTime input) { public bool IsLeapYearQuestion { get { var day = input.AddMonths(1); return day.AddDays(28).Day > 28; } } } а такой код проверки на високосный год имеет право на жизнь?
Haggen Уровень 22
24 февраля 2026
Необходимо обновить лекцию. DataTime.Now.Year = 2026)))))
Slevin Уровень 57
2 февраля 2026
Вот вам пример с использованием Коллекций и LINQ которые мы еще ВООБЩЕ НЕ РАССМАТРИВАЛИ, надеемся вам понятно что такое лямбда функция, откуда мы высрали какое-то "v =>", короче наслаждайтесь.
Yurii N Уровень 66
7 января 2026
Странно утверждать, что нельзя добавлять свойства (предыдущая лекция):

Ограничения Extension Methods
Нельзя добавлять новые поля, свойства или события: 
Можно добавлять только методы. Вы не можете использовать их для добавления
 нового состояния к существующему типу.
А теперь говорить, что уже можно.
Паша Гриц Уровень 1
31 июля 2025
это конечно интересно писать код, который невозможно проверить на работоспособность, но который проходит проверку :)