Форум программистов, компьютерный форум, киберфорум
C#: Базы данных
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/3: Рейтинг темы: голосов - 3, средняя оценка - 5.00
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,110
Записей в блоге: 2

Entity Framework: Запись значений в иммутабельные типы

05.01.2025, 00:05. Показов 1083. Ответов 11

Студворк — интернет-сервис помощи студентам
Столкнулся с ситуацией, которую даже не знаю куда копать.
Есть иммутабельный тип-сущность:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
namespace SqlLiteExamples
{
    public class ExampleData
    {
        private static int num;
 
        /// <summary>Номер экземпляра для тестирования.</summary>
        public int Num { get; } = ++num;
 
        public override string ToString() => $"{{{Num}: {Id}, {Text}, {IsSelect}}}";
 
        public ExampleData(int id, string text, bool isSelect)
        {
            Id = id;
            Text = text;
            IsSelect = isSelect;
        }
 
        public int Id { get; }
 
        public string Text { get; }
 
        public bool IsSelect { get; }
    }
}
Простой контекст для SQLite 9.0:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using Microsoft.EntityFrameworkCore;
 
namespace SqlLiteExamples
{
    public class ExampleContext : DbContext
    {
        public DbSet<ExampleData>? Examples { get; }
 
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            _ = optionsBuilder.UseSqlite("Data Source=example.db");
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder
                .Entity<ExampleData>()
                .HasKey(e => e.Id);
 
            modelBuilder
                .Entity<ExampleData>()
                .Property(e => e.Text);
 
            modelBuilder
                .Entity<ExampleData>()
                .Property(e => e.IsSelect);
 
            modelBuilder
                .Entity<ExampleData>()
                .HasData("Форум программистов C#. Использование технологии ADO.NET и работа с базами данных в .NET".Split().Select((t, i) => new ExampleData(i + 1, t, false)));
 
            base.OnModelCreating(modelBuilder);
        }
 
    }
}
Тест изменения сущности:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using Microsoft.EntityFrameworkCore;
using SqlLiteExamples;
using System.Collections.ObjectModel;
{
    // Создание постоянного контекста и загрузка локального кеша.
    ExampleContext context = new ExampleContext();
    context.Database.EnsureDeleted();
    context.Database.EnsureCreated();
    DbSet<ExampleData> examples = context.Set<ExampleData>();
    examples.Load();
    ObservableCollection<ExampleData> localExamples = examples.Local.ToObservableCollection();
 
    // Получение сущности по ключу.
    ExampleData inst = examples!.Find(3)!;
    Console.WriteLine(inst);
 
    // Сорздание временного контекста.
    using (ExampleContext temp = new ExampleContext())
    {
        // Обновление записи в таблице.
        ExampleData e = new(inst.Id, inst.Text, true);
        temp.Set<ExampleData>().Update(e);
        temp.SaveChanges();
    }
 
    // Получение обновлённой сущности.
 
    var ent = examples.Local.FindEntry(3);
    ent.Reload();
    ExampleData upd = examples!.Find(3)!;
 
    Console.WriteLine(inst);
    Console.WriteLine(upd);
    Console.WriteLine(ReferenceEquals(inst, upd));
}
Вывод результата:
Code
1
2
3
4
{16: 3, C#., False}
{16: 3, C#., True}
{16: 3, C#., True}
True
Я в шоке!
Изменено значение иммутабельного свойства!
Каким образом? Здесь даже рефлексия не поможет!
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
05.01.2025, 00:05
Ответы с готовыми решениями:

В чем разница между Entity Framework и Entity Framework Core?
В чем разница (если она есть) между entity framework и entity framework core?

Не сохраняет запись в БД Entity Framework
Здравствуйте. Проблема заключается в том, что при добавлении новой записи, она не записывается в БД, хотя программа проходит без ошибок и...

Как средствами entity framework удалить запись?
Подскажите, вот имеется у меня модель, а как средствами entity framework ужалить из таблицы запись по определенному условию? Пробовал...

11
Эксперт .NET
 Аватар для Rius
13163 / 7722 / 1679
Регистрация: 25.05.2015
Сообщений: 23,522
Записей в блоге: 14
05.01.2025, 00:30
Если вопрос в том, как избежать, то можно отсоединить от отслеживания:
C#
1
ExampleData inst = examples!.AsNoTracking().First(x => x.Id == 3)!;
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,110
Записей в блоге: 2
05.01.2025, 00:53  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
Если вопрос в том, как избежать, то можно отсоединить от отслеживания:
Не.
Как избежать - это другое.
Мне нужно обновить сущность в локальном кеше представляемом через ObservableCollection:
C#
11
ObservableCollection<ExampleData> localExamples = examples.Local.ToObservableCollection();
Как сделать через замену экземпляра я представляю.
Но в данном вопросе, мне не понятен сам механизм изменения иммутабельного свойства.

Добавлено через 4 минуты
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Как сделать через замену экземпляра я представляю.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using Microsoft.EntityFrameworkCore;
using SqlLiteExamples;
using System.Collections.ObjectModel;
{
    // Создание постоянного контекста и загрузка локального кеша.
    ExampleContext context = new ExampleContext();
    context.Database.EnsureDeleted();
    context.Database.EnsureCreated();
    DbSet<ExampleData> examples = context.Set<ExampleData>();
    examples.Load();
    ObservableCollection<ExampleData> localExamples = examples.Local.ToObservableCollection();
 
    // Получение сущности по ключу.
    ExampleData inst = examples!.Find(3)!;
    Console.WriteLine(inst);
 
    // Сорздание временного контекста.
    ExampleData e = new(inst.Id, inst.Text, true);
    using (ExampleContext temp = new ExampleContext())
    {
        // Обновление записи в таблице.
        temp.Set<ExampleData>().Update(e);
        temp.SaveChanges();
    }
 
    // Получение обновлённой сущности.
 
    var ent = examples.Local.FindEntry(3);
    ent.Reload();
    ExampleData upd = examples!.Find(3)!;
 
    Console.WriteLine(inst);
    Console.WriteLine(upd);
    Console.WriteLine(ReferenceEquals(inst, upd));
    Console.WriteLine();
 
 
    // Замена сущности в локальном кеше:
    for (int i = 0; i < 10; i++)
    {
        if (localExamples[i].Id == e.Id )
        {
            localExamples[i] = e;
            examples.Entry(e).State = EntityState.Unchanged;
            break; 
        }
    }
    upd = examples!.Find(3)!;
    Console.WriteLine(ReferenceEquals(inst, upd));
0
HF
 Аватар для HF
1316 / 895 / 200
Регистрация: 09.09.2011
Сообщений: 2,690
Записей в блоге: 2
05.01.2025, 01:28
(если я что-то не знаю, то узнаю если в чём-то ошибся я уже давно с EF не работаю )
Но разве здесь не работает так как и должно работать? Сущность изменили в БД, Observable коллекция это отловила и обновила. Сущность изменилась везде. Что не так?
Это же EF, и все объекты - прокси-объекты, а не конечные, "неизменяемые". Скорее всего не свойство изменилось, а весь объект подменился. Ради интереса получи какой-то IntPtr или что-то круто-уникальное. И тогда уже сравни. Убедишься (по моей теории) что это разные объекты.

Добавлено через 7 минут
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Мне нужно обновить сущность в локальном кеше представляемом через ObservableCollection
Кстати, в первом примере не было результата получения.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,110
Записей в блоге: 2
05.01.2025, 01:46  [ТС]
Цитата Сообщение от HF Посмотреть сообщение
Но разве здесь не работает так как и должно работать?
Обратите внимание на конструктор. Там стоит счётчик экземпляров.
Так как экземпляры иммутабельны, то для изменения изменения свойства, нужно создать новый экземпляр. Но экземпляр остаётся тот же (с номером #16, в данном примере).
Вот именно механизм изменения иммутабельного свойства мне и не понятен.

Цитата Сообщение от HF Посмотреть сообщение
Сущность изменили в БД, Observable коллекция это отловила и обновила.
В БД изменилась запись.
У сущности в локальном кеше изменилось свойство.
Но так как экземпляр остался прежним, то ObservableCollection не обновляется.

Цитата Сообщение от HF Посмотреть сообщение
Это же EF, и все объекты - прокси-объекты, а не конечные, "неизменяемые".
Нет.
Если получить типы, то у всех будет исходный тип. Никакаой виртуализации здесь нет. Можно даже запечатать тип.
C#
1
2
3
4
5
6
7
8
namespace SqlLiteExamples
{
    public sealed class ExampleData
    {
        private static int num;
 
        /// <summary>Номер экземпляра для тестирования.</summary>
        public int Num { get; } = ++num;
C#
29
30
31
32
33
34
    // Получение обновлённой сущности.
 
    var ent = examples.Local.FindEntry(3);
    ent.Reload();
    ExampleData upd = examples!.Find(3)!;
    Console.WriteLine(upd.GetType());
Code
1
2
3
4
5
6
7
{16: 3, C#., False}
SqlLiteExamples.ExampleData
{16: 3, C#., True}
{16: 3, C#., True}
True
 
False
Цитата Сообщение от HF Посмотреть сообщение
Скорее всего не свойство изменилось, а весь объект подменился.
Тогда изменился бы номер экземпляра.

Цитата Сообщение от HF Посмотреть сообщение
Ради интереса получи какой-то IntPtr или что-то круто-уникальное.
Я же провожу сравнение ссылок на экземпляры:
C#
38
    Console.WriteLine(ReferenceEquals(inst, upd));
Экземпляр один и тот же.
Кроме того, предыдущее получение из кеша я сохраняю в локальной переменой: ExampleData inst = examples!.Find(3)!;.
И EF ни как не может повлиять на внешнюю локальную переменную.
Поэтому изменение происходит в том же самом экземпляре - это без вариантов.

Цитата Сообщение от HF Посмотреть сообщение
Кстати, в первом примере не было результата получения.
Было:
C#
33
    ExampleData upd = examples!.Find(3)!;
0
HF
 Аватар для HF
1316 / 895 / 200
Регистрация: 09.09.2011
Сообщений: 2,690
Записей в блоге: 2
05.01.2025, 02:01
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Было
Разве Observable коллекция это не localExamples ?

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Кроме того, предыдущее получение из кеша я сохранению в локальной переменой: ExampleData inst = examples!.Find(3)!;.
И EF ни как не может повлиять на внешнюю локальную переменную.
Снова "разве". Разве никак? Или я что-то не так понимаю или перевод не верный, но вот цитата

https://learn.microsoft.com/ru... work-6.2.0
Возвращает объект , ObservableCollection<T> представляющий локальное представление всех добавленных, без изменений и измененных сущностей в этом наборе. Это локальное представление остается синхронизированным по мере добавления или удаления сущностей из контекста. Аналогичным образом добавляемые или удаляемые из этого локального представления сущности автоматически добавляются в контекст или удаляются из контекста.
Прочитал "по диагонали", и прочитал только последнее предложение, где как я понимаю - автоматически меняются, добавляются и удаляются. Короче говоря - полностью синхронизация состояния.
Не?

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Так как экземпляры иммутабельны, то для изменения изменения свойства, нужно создать новый экземпляр. Но экземпляр остаётся тот же (с номером #16, в данном примере).
ORMки работают очень глубоко, и им даже часто всё равно на "иммунабельность". Они могут влёгкую изменять приватные поля, поэтому я и не удивлюсь что он всё-таки увидел что сущность изменилась и просто обновил изменённые значения.

Самым простым решением здесь всё-таки использовать нотрекинг. Поставь его глобально (на весь контекст) и снова проверь. Результат будет так же?

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Можно даже запечатать тип.
Эмм... (вижу sealed модификатор у класса)
Унаследоваться нельзя. Но не "запечатать", заблочить, заимунировать и т.п. же
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,110
Записей в блоге: 2
05.01.2025, 02:15  [ТС]
Цитата Сообщение от HF Посмотреть сообщение
Разве Observable коллекция это не localExamples ?
Да.
И при изменении в локальном кеше происходит изменения и в ней.
Но так как экземпляр не заменяется, то изменения не происходит.

Цитата Сообщение от HF Посмотреть сообщение
Снова "разве". Разве никак? Или я что-то не так понимаю или перевод не верный, но вот цитата
/Это иммутабельный тип.
Вот я именно и интересуюсь, за механизм используемый EF для изменения свойства "только для чтения".

Цитата Сообщение от HF Посмотреть сообщение
Короче говоря - полностью синхронизация состояния.
Для мутабельных типов - вопросов нет.
А каким образом для иммутабельных? Я думал, что через замену экземпляра.
Оказалось, что каким-то неведомым образом меняется свойство "только для чтения".

Цитата Сообщение от HF Посмотреть сообщение
ORMки работают очень глубоко, и им даже часто всё равно на "иммунабельность". Они могут влёгкую изменять приватные поля, поэтому я и не удивлюсь что он всё-таки увидел что сущность изменилась и просто обновил изменённые значения.
Как?
Обычно меняют через рефлексию.
Но в данном случае, рефлексия не помощник:
C#
56
57
    System.Reflection.PropertyInfo propr = typeof(ExampleData).GetProperty(nameof(ExampleData.IsSelect))!;
    propr.SetValue(upd, false); // Исключение "System.ArgumentException: "Property set method not found.""
Цитата Сообщение от HF Посмотреть сообщение
Унаследоваться нельзя. Но не "запечатать", заблочить, заимунировать и т.п. же
sealed - это и есть "запечатать" для наследования.
То что свойство иммутабельно это следует из его объявления:
C#
23
        public bool IsSelect { get; }
В типе все свойства "только для чтения" - такие типы называются иммутабльными. Сам тип запечатанный - поэтому через наследование внедрить каким-то хитрым образом мутабельность, тоже не выйдет.
Кроме того типы экземпляров считываются и выводятся. Поэтому гарантированно, что здесь точно нет наследования.
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,233
05.01.2025, 09:11
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Но так как экземпляр не заменяется, то изменения не происходит.
А разве реакция на изменения это не история про INotifyPropertyChanged?
ObservableCollection дает только реакцию на изменение самой коллекции (добавление/удаление элемента).

Это на фронте веб-фреймворки (react, vue, вроде angular в недефолтном change detection режиме) перерисовывают если ссылки обновляются.

Добавлено через 6 минут
Цитата Сообщение от IamRain Посмотреть сообщение
ObservableCollection дает только реакцию на изменение самой коллекции (добавление/удаление элемента).
However, to set up dynamic bindings so that insertions or deletions in the collection update the UI automatically, the collection must implement the INotifyCollectionChanged interface. This interface exposes the CollectionChanged event that must be raised whenever the underlying collection changes.

WPF provides the ObservableCollection<T> class, which is a built-in implementation of a data collection that exposes the INotifyCollectionChanged interface
И дальше отсылка на:
The individual data objects within the collection must satisfy the requirements described in the Binding Sources Overview.
И далее там уже про два способа уведомлений упоминается:
Implement the INotifyPropertyChanged interface.

This is the recommended mechanism for notifications. The INotifyPropertyChanged supplies the PropertyChanged event, which the binding system respects. By raising this event, and providing the name of the property that changed, you'll notify a binding target of the change.

Implement the PropertyChanged pattern.

Each property that needs to notify a binding target that it's changed, has a corresponding PropertyNameChanged event, where PropertyName is the name of the property. You raise the event every time the property changes.
Элд Хасп, странные ожидания у вас, как по мне.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,110
Записей в блоге: 2
05.01.2025, 10:22  [ТС]
Цитата Сообщение от IamRain Посмотреть сообщение
А разве реакция на изменения это не история про INotifyPropertyChanged?
ObservableCollection дает только реакцию на изменение самой коллекции (добавление/удаление элемента).
Совершенно верно.
И вопрос у меня не про отсутствие реакции ObservableCollection.

Вопрос у меня: Каким образом, используя какой механизм EF изменяет значение свойств "только для чтения" (иммутабельных свойств)?

Для примера, попробуйте "руками" повторить, то что желает EF: создайте любую коллекцию из ExampleData измените значение любого свойства одного из элементов этой коллекции, не заменяя сам экземпляр.
Напрямую или используя рефлексию - это не выйдет сделать. Но у EF это получается сделать.
0
Эксперт .NET
 Аватар для Rius
13163 / 7722 / 1679
Регистрация: 25.05.2015
Сообщений: 23,522
Записей в блоге: 14
05.01.2025, 10:26
Цитата Сообщение от Элд Хасп Посмотреть сообщение
не заменяя сам экземпляр
Почему не заменяя? EF Core разве не может создать новый экземпляр на основе старых и новых данных и поместить его в то же место?

Добавлено через 1 минуту
Судя по до сих пор не закрытой теме https://github.com/dotnet/efcore/issues/11457 , там всё не просто.
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,110
Записей в блоге: 2
05.01.2025, 10:27  [ТС]
Лучший ответ Сообщение было отмечено Элд Хасп как решение

Решение

Я даже изменил реализацию сущности, чтобы EF не обращался к анонимным автополям:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
namespace SqlLiteExamples
{
    public sealed class ExampleData
    {
        private static int num;
 
        // Приватный массив для инкапсуляции значений.
        private object[] values = new object[4];
 
        /// <summary>Номер экземпляра для тестирования.</summary>
        public int Num => (int) values[0];
 
        public override string ToString() => $"{{{Num}: {Id}, {Text}, {IsSelect}}}";
 
        public ExampleData(int id, string text, bool isSelect)
        {
            values[0] = ++num;
            values[1] = id;
            values[2] = text;
            values[3] = isSelect;
        }
 
        public int Id => (int)values[1];
 
        public string Text => (string)values[2];
 
        public bool IsSelect => (bool)values[3];
    }
}
И теперь EF выкинул исключение:
System.InvalidOperationException: "No backing field could be found for property 'ExampleData.IsSelect' and the property does not have a setter."
Из этого следует, что EF использует неофициальные соглашения об именах автополей, чтобы через рефлексию изменить значения иммутабельных свойств.
Честно говоря, хлипкая конструкция. Имена могут быть другими, поля могут быть тоже объявлены "только для чтения".

Вопрос на свой ответ я нашёл.
Теперь буду думать как более оптимально реализовать то что мне нужно. Но это уже в другой теме.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,110
Записей в блоге: 2
05.01.2025, 11:05  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
EF Core разве не может создать новый экземпляр на основе старых и новых данных и поместить его в то же место?
Может, но не делает так.

Добавлено через 37 минут
Цитата Сообщение от Rius Посмотреть сообщение
Судя по до сих пор не закрытой теме https://github.com/dotnet/efcore/issues/11457 , там всё не просто.
Добавил туда свой коммент.
https://github.com/dotnet/efco... 2571534279
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
05.01.2025, 11:05
Помогаю со студенческими работами здесь

Как удалить запись из datagridview? Entity Framework
Проблема следующая: Мне надо перенести выбираемые пользователем записи студентов из одного datagridview в другой. Есть две таблицы ...

Entity Framework. Проблема с получением значений навигационными свойствами
Здравствуйте! Есть три класса, связаных через Базу даных: public class Animal : INotifyPropertyChanged { public int Id...

Entity FrameWork заменяет запись копией при сохранении
Здравствуйте, суть в следующем, есть таблица которая берез значения из другой. Пытаюсь сделать возможность редактирования. Если...

Entity Framework не удаляется связанная по внешнему ключу запись
Здравствуйте! Я пытаюсь удалить элемент из коллекции UsersVaults объекта типа User, но получаю Exception (текст ошибки и код в...

Удалить запись из базы данных с использованием Entity Framework
Я использую Entity Framework. Я хотел бы удалить запись основанную на primary key, но я не хочу обращаться к базе данных перед удалением...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
12
Ответ Создать тему
Новые блоги и статьи
Хочу заставить корпорации вкладываться в здоровье сотрудников: делаю мат модель здравосохранения
anaschu 22.03.2026
e7EYtONaj8Y Z4Tv2zpXVVo https:/ / github. com/ shumilovas/ med2. git
1С: Программный отбор элементов справочника по группе
Maks 22.03.2026
Установка программного отбора элементов справочника "Номенклатура" из модуля формы документа. В качестве фильтра для отбора справочника служит группа номенклатуры. Отбор по наименованию группы. . .
Как я обхитрил таблицу Word
Alexander-7 21.03.2026
Когда мигает курсор у внешнего края таблицы, и нам надо перейти на новую строку, а при нажатии Enter создается новый ряд таблицы с ячейками, то мы вместо нервных нажатий Энтеров мы пишем любые буквы. . .
Krabik - рыболовный бот для WoW 3.3.5a
AmbA 21.03.2026
без регистрации и смс. Это не торговля, приложение не содержит рекламы. Выполняет свою непосредственную задачу - автоматизацию рыбалки в WoW - и ничего более. Однако если админы будут против -. . .
1С: Программный отбор элементов справочника по значению перечисления
Maks 21.03.2026
Установка программного отбора элементов справочника "Сотрудники" из модуля формы документа. В качестве фильтра для отбора служит значение перечислений. / / Событие "НачалоВыбора" реквизита на форме. . .
Переходник USB-CAN-GPIO
Eddy_Em 20.03.2026
Достаточно давно на работе возникла необходимость в переходнике CAN-USB с гальваноразвязкой, оный и был разработан. Однако, все меня терзала совесть, что аж 48-ногий МК используется так тупо: просто. . .
Оттенки серого
Argus19 18.03.2026
Оттенки серого Нашёл в интернете 3 прекрасных модуля: Модуль класса открытия диалога открытия/ сохранения файла на Win32 API; Модуль класса быстрого перекодирования цветного изображения в оттенки. . .
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru