Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.69/48: Рейтинг темы: голосов - 48, средняя оценка - 4.69
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18

Преобразование типов в ограниченных обобщенных классах

21.08.2020, 17:08. Показов 10689. Ответов 28

Студворк — интернет-сервис помощи студентам
Здравствуйте, друзья.
Изучаю C#. Соответственно экспериментирую с кодом в процессе изучения.
Такой вопрос.
Имеется допустим иерархия классов:
C#
1
2
3
4
5
class Base 
{
    public int Num { get; set; } 
}
class Derived : Base { }
Так же имеется обобщенный класс ограниченный типом Base. В нем определен метод Method, который принимает аргументом объект типа T и пытается явно преобразовать его в Derived (не обращайте, пожалуйста, внимание
на несуразность примера, это просто для экспериментов). Так вот выдает ошибку о невозможности преобразования:
C#
1
2
3
4
5
6
7
8
9
class Generic<T> where T : Base
{
    public Method(T obj)
    {
        Derived d = (Derived)obj; // Ошибка: Невозможно преобразовать T в Derived
 
        Console.WriteLine(obj.Num);
    }
}
Я представлял себе ограничения как способ работать с любым производным от ограничивающего типа типом как с типом ограничения (в нашем случае Base). Ведь к свойству Num мы ведь получаем возможность обратиться из обобщенного класса. Я решил, что компилятор знает теперь о потомках Base и даст попробовать явно преобразовать его к какому нибудь потомку. Но он не дает. Хотя по логике все должно быть норм (по крайней мере, моей логике).
Можете, пожалуйста, объяснить такое поведение? В чем я ошибаюсь?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
21.08.2020, 17:08
Ответы с готовыми решениями:

Арифметические действия в обобщенных классах
Здравствуйте! Нужен пример, как ограничить обобщенный тип для написания универсального класса так, чтобы можно было выполнять действия...

Взаимосвязь типов в двух обобщённых классах
Здравствуйте. Возникли затруднения в решении следующей задачи: Как увязать типы в разных классах? Например, при создании объектов...

Преобразование типов данных в классах
если например есть такой код: class TIME { int a; } TIME object; object = 10;

28
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
21.08.2020, 17:26
Лучший ответ Сообщение было отмечено loyalist28 как решение

Решение

Цитата Сообщение от loyalist28 Посмотреть сообщение
В чем я ошибаюсь?
Наверное в том, что предполагаете что раз у обобщенного типа есть ограничение на наследование от Base, то тип переменной obj будет Base, а не T.

В наследовании бывают ветвления, если два типа находятся на одном уровне иерархии, то один из них нельзя привести к другому.
Полиморфические преобразования работают только вверх или вниз по иерархии, горизонтально не работают.
Поскольку обобщенный тип должен работать со всеми классами, удовлетворяющими условие наследования от Base, постольку компилятор должен предполгать, что возможно приведение типов на одном уровне, о чем и сообщает.

Пример той же ошибки без обобщений:
C#
1
2
3
4
5
6
class A {}
class B : A {}
class C : A {}
 
C c = new C();
B b = (B)c; // Та же самая ошибка.
Если ваш тип был сконструирован как Generic<C>, то возникнет та же ситуация: в метод передается параметр типа С, который вы пытаетесь привести к типу В.
2
1595 / 600 / 185
Регистрация: 05.12.2015
Сообщений: 970
21.08.2020, 17:53
Цитата Сообщение от loyalist28 Посмотреть сообщение
Я представлял себе ограничения как способ работать с любым производным от ограничивающего типа типом как с типом ограничения (в нашем случае Base)
ты не дошел еще до виртуальных функций
тогда этот вопрос не задал бы
обобщения как правило используют для вызова из производных классов виртуальных функций и свойств, которые определяются в базовом, зачастую пустые.
1
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
23.08.2020, 09:53  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
Наверное в том, что предполагаете что раз у обобщенного типа есть ограничение на наследование от Base, то тип переменной obj будет Base, а не T.
Ну если это не так, то почему данный код ведет себя таким образом?
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
using System;
 
class Base
{
    public void ShowClassName()
    {
        Console.WriteLine("Base");
    }
}
class Derived : Base
{
    public new void ShowClassName()
    {
        Console.WriteLine("Derived");
    }
}
class Generic<T> where T : Base
{
    public void Method(T obj)
    {
        obj.ShowClassName();
    }
}
 
class Program
{
    static void Main(string[] args)
    {   
        Derived d = new Derived();
        Generic<Derived> gDerived = new Generic<Derived>();
 
        gDerived.Method(d);      //Base!!!
    }
}
Срабатывает реализация из Base, хоть и Generic<T> типизирован Derived и в Method() передается объект типа Derived.
0
 Аватар для Enifan
1845 / 1187 / 501
Регистрация: 14.10.2018
Сообщений: 3,204
23.08.2020, 11:44
Лучший ответ Сообщение было отмечено loyalist28 как решение

Решение

Цитата Сообщение от loyalist28 Посмотреть сообщение
Срабатывает реализация из Base
Давайте подумаем почему так произошло.
Объект d на 29 строке кода имеет 2 публичных метода ShowClassName() с одинаковым названием. Один метод из базового класса, другой из текущего.
Обобщение where T : Base может принимать в качестве T класс Base или любой унаследованный от него. Данное обобщение работает именно с классом Base и все паблик поля, свойства и методы от данного класса он может вызывать внутри себя.
И вот происходит вызов метода на 21 строке, но какой? ведь у объекта d их по факту 2, но обобщение может работать только с одним, потому и происходит вызов метода класса Base, так как о производных методах обобщение ничего не знает.
0
1123 / 794 / 219
Регистрация: 15.08.2010
Сообщений: 2,185
23.08.2020, 11:48
Цитата Сообщение от loyalist28 Посмотреть сообщение
то почему данный код ведет себя таким образом?
благодаря new ваш код можно переписать как
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
        class Base
        {
            public void Foo()
            {
                Console.WriteLine("Base");
            }
        }
        class Derived : Base
        {
            public void Qwerty()
            {
                Console.WriteLine("Derived");
            }
        }
        class Generic<T> where T : Base
        {
            public void Method(T obj)
            {
                obj.Foo();
            }
        }
 
        private static void Main(string[] args)
        {
            Derived d = new Derived();
            Generic<Derived> gDerived = new Generic<Derived>();
 
            gDerived.Method(d);      //Base!!!
        }
думаю тут очевидно, почему вызывается Foo
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
23.08.2020, 12:32
Лучший ответ Сообщение было отмечено loyalist28 как решение

Решение

loyalist28, все по той же причине: обобщенный метод должен работать со всеми типами и не у всех будет затенение метода, следовательно используется базовая реализация, хоть по факту переменная и будет иметь тип Derived.
1
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
23.08.2020, 14:44  [ТС]
Спасибо, друзья. Теперь все понятно.
0
1595 / 600 / 185
Регистрация: 05.12.2015
Сообщений: 970
23.08.2020, 20:45
Цитата Сообщение от loyalist28 Посмотреть сообщение
Теперь все понятно.
такой подход на практике лично я не использую - это бессмысленно
в первом посте я написал для чего это надо - виртуальные функции
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
class Program
    {
        class Base
        {
            public virtual void ShowClassName()
            {
                Console.WriteLine( "Base" );
            }
        }
        class Derived : Base
        {
            public override void ShowClassName()
            {
                Console.WriteLine( "Derived" );
            }
        }
        class Generic<T> where T : Base
        {
            public void Method( T obj )
            {
                obj.ShowClassName();
            }
        }
 
        static void Main( string[] args )
        {
            Derived d = new Derived();
            Generic<Derived> gDerived = new Generic<Derived>();
            gDerived.Method( d );      //?????????????????
            Console.ReadKey();
        }
    }
0
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
24.08.2020, 02:05  [ТС]
Цитата Сообщение от proa33 Посмотреть сообщение
в первом посте я написал для чего это надо - виртуальные функции
Вот если честно, этот вопрос и родился во время изучения использования обобщений для вызова переопределенных методов в потомках. Я решил посмотреть, что будет если не оверрайдить метод, а затенить. Увидел, что вызывается реализация из Base и это немного поломало мои убеждения о происходящем) Так то я примерно так и представлял, как мне подсказали товарищи в постах выше. Но тут начали закрадываться сомнения, что а не используется ли вместо любого T, которым типизируешь ограниченный обобщенный класс, ограничивающий тип. Решил попробовать принудительно преобразовать объект типа T. Получил ошибку по поводу которой и появилась эта тема. Но теперь осознал, что действительно в параметр типа T могут быть переданы типы с одного уровня иерархии наследования с Derived. Компилятор предполагает такую возможность и не дает нам провести не типобезопасную операцию.

Добавлено через 14 минут
Ну и заодно понял очень полезную вещь, что при создании класса Generic<T>, в его методе Method() сразу прописывается ссылка на реализацию ShowClassName из Base, так как Generic<T> имеет информацию только о нем, а о его потомках ничего не знает. Тогда действительно, что даже если передать в Method() объект типа Derived,положить его в переменную типа Derived, то все равно реализация из Base будет вызвана. Хотя это и немного необычно )
0
1522 / 507 / 126
Регистрация: 09.01.2018
Сообщений: 1,536
24.08.2020, 08:32
Цитата Сообщение от loyalist28 Посмотреть сообщение
Но теперь осознал, что действительно в параметр типа T могут быть переданы типы с одного уровня иерархии наследования с Derived. Компилятор предполагает такую возможность и не дает нам провести не типобезопасную операцию.
В таком случае компилятор выдавал бы ошибку при любой попытке приведения базового типа к производному. Однако следующий код прекрасно компилируется:

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
    class A 
    { 
        public void MethodA()
        {
 
        }
    }
 
    class B : A 
    { 
        public void MethodB()
        {
 
        }
    }
 
    class C : A { };
 
....
 
        void Method(A a)
        {
            //Compile but runtime error if a is not in fact B
            //Unable to cast object of type 'A' to type 'B'."
            var b = (B)a;
        }
Ошибка произойдет только в рантайме, в случае, если фактический тип, лежащий в переменной 'a' окажется не 'B'.
На самом деле, такие преобразования разрешены в спецификации языка, поэтому компилятор не выдает ошибок.

А для параметров типа разрешены не все преобразования. В частности, преобразование 'T' в производный тип не разрешается. Потому компилятор сразу выдает ошибку.
Вот небольшой список разрешенных преобразований:

Code
1
2
3
4
5
         TBase => T
         BaseOfTBase => T (e.g. object => T)
         T => Tbase
         T => BaseOfTBase
         oblect => TAny
Отсюда можно найти способ провести преобразование типа:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    class Generic<T> where T : A
    {
        public void Method(T a)
        {
            //var b = (B)a; //compile error
 
            var o = (B)(object)a; //unsafe but compile
 
            //safe
            if (a is B b)
            {
                b.MethodB();
            }
  
        }
    }
В первой попытке будет ошибка компиляции, по причинам описанным выше. Вторая скомпилируется, поскольку в ней переменная типа сначала преобразовывается к типу 'object', что разрешено, а затем тип 'object' преобразовывается к производному типу, что также не противоречит спецификации.

В третьей попытке все прекрасно, поскольку используется безопасное приведение типов.
Подробнее о преобразовании типов и параметров типа можно прочитать по ссылкам ниже (спецификация - Implicit, Explicit):

https://docs.microsoft.com/en-... parameters

https://docs.microsoft.com/en-... parameters

Внимание: Не читайте на русском языке.


Цитата Сообщение от loyalist28 Посмотреть сообщение
то все равно реализация из Base будет вызвана. Хотя это и немного необычно
Ничего необычного. Определение невиртуального метода выполняется по типу переменной, а у нее тип базового класса. Соответственно метод базового класса и вызывается. Чтобы вызвать реализацию наследника, необходимо приведение обратно к производному типу.
2
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
24.08.2020, 13:32  [ТС]
Цитата Сообщение от escoult Посмотреть сообщение
Ничего необычного. Определение невиртуального метода выполняется по типу переменной, а у нее тип базового класса. Соответственно метод базового класса и вызывается. Чтобы вызвать реализацию наследника, необходимо приведение обратно к производному типу.
Про какой невиртуальный метод из моего примера вы говорите? По типу какой переменной? Я типизировал объект Generic<T> типом Derived, значит Method(T obj) стал Method(Derived obj), а не Method(Base obj). Сначала я предполагал, то о чем вы говорите: что в ограниченном обобщенном классе используется тип ограничения. Но выше были ответы, что это не так. Вы же сейчас утверждаете обратное. Или я что то не так понял?

Добавлено через 4 минуты
Цитата Сообщение от escoult Посмотреть сообщение
А для параметров типа разрешены не все преобразования. В частности, преобразование 'T' в производный тип не разрешается. Потому компилятор сразу выдает ошибку.
Спасибо, очень полезная информация!
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
24.08.2020, 14:15
Цитата Сообщение от loyalist28 Посмотреть сообщение
Или я что то не так понял?
Во время выполнения, когда будет сконструирован конкретный тип, переменная будет иметь тип Derived — здесь вы все правильно поняли и это можно легко проверить:
C#
1
2
3
4
public void Method(T a)
{
    Console.WriteLine(typeof(T).Name); // Не путать с a.GetType()!
}
А вот на стадии компиляции компилятору нужно определить: к каким членам позволять обращаться на переменной типа Т и какие преобразования с ней делать. Поскольку компилятор понятия не имеет о фактическом типе Т во время выполнения и должен удостовериться, что любой код будет работать корректно вне зависимости от Т, то он оперирует только той информацией, которую имеет на данный момент: о типе Base, потому и адрес метода подставляет тот, который в таблице методов Base.

Затенение создает новую запись в таблице методов типа, переопределение виртуального метода меняет адрес реализации уже существующей записи — той, которую создал класс, объявивший виртуальный метод.
1
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
25.08.2020, 01:49  [ТС]
Спасибо, очень доходчиво и познавательно.
0
1522 / 507 / 126
Регистрация: 09.01.2018
Сообщений: 1,536
25.08.2020, 03:44
Цитата Сообщение от loyalist28 Посмотреть сообщение
Сначала я предполагал, то о чем вы говорите: что в ограниченном обобщенном классе используется тип ограничения. Но выше были ответы, что это не так. Вы же сейчас утверждаете обратное. Или я что то не так понял?
Что же тогда используется, если не ограничивающий тип? Других данных у компилятора нет.
Вы правильно предполагали. Привязка методов, даже виртуальных, даже в обобщенных классах, выполняется во время компиляции. А во время компиляции тип переменной - это ограничивающий тип. Если метод виртуальный, то для определения метода используется фактический тип объекта, на который ссылается переменная. (привязка, тем не менее, все также выполняется во время компиляции, там другой механизм).

Когда вы создаете в коде закрытый тип (имеющий действительный тип данных), компилятор лишь проверяет, что параметр типа соответствует указанным ограничениям. И все. К слову, если ограничивающий тип не определен, то он имеет тип 'object'. Т.е. ограничивающий тип есть всегда и он известен во время компиляции. Однако типом 'Derived' он станет только во время выполнения. Но в это время метод уже привязан. Получается, что изменяя действительный тип данных в коде, вы не можете повлиять на определение вызываемого метода, если он не виртуальный. Это всегда будет метод ограничивающего типа. Тем не менее, один интересный выход все же есть.

Если фактический тип будет известен лишь во время выполнения, то для вызова метода производного класса можно использовать переменную типа 'dynamic'. Этот тип может ссылаться на объект любого типа, а проверка на соответствие будет отложена до времени выполнения. И это свойство можно использовать.

C#
1
2
3
4
5
6
7
8
9
10
11
12
    class Generic<T> where T : A
    {
        public void Method(T a)
        {
            //compile time, a is A
            a.MethodA(); //A
 
            //runtime, d is B
            dynamic d = a;
            d.MethodA(); //new B
        }
    }
1
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
25.08.2020, 03:59  [ТС]
Спасибо, очень доходчиво и познавательно.
Цитата Сообщение от escoult Посмотреть сообщение
Что же тогда используется, если не ограничивающий тип? Других данных у компилятора нет.
Вот тоже не пойму, разве компилятор не в курсе о иерархии наследования ограничивающего типа? Мы же в коде все это указываем и он знает, какие типы наследуют от ограничивающего типа. Почему он не использует эту информацию во время компиляции применительно к ограниченным обобщенным типам?
0
1522 / 507 / 126
Регистрация: 09.01.2018
Сообщений: 1,536
25.08.2020, 04:18
Цитата Сообщение от loyalist28 Посмотреть сообщение
Почему он не использует эту информацию во время компиляции применительно к ограниченным обобщенным типам?
Причины очевидно есть и лежат где то в глубинах работы CLR. Я не знаю обо всех. Необходим строгий порядок вызовов. Вот один из известных примеров:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   class A 
    { 
        public int MethodA()
        {
            return 0;
        }
    }
 
    class B : A 
    {
        public new string MethodA()
        {
            return "new B";
        }
    }
Что произойдет, если в обобщенном классе компилятор определит фактический тип объекта и вызовет метод класса 'B'?
Метод базового класса должен возвращать совсем другой тип значения. Но это же универсальный шаблон.

Другой пример. Если бы так оно было, то как вызвать нужную реализацию, имея экземпляр производного класса? Выполняем приведение к одному из базовых, но компилятор тем не менее вызывает метод по фактическому типу объекта.

Скорее всего есть еще причины и не мало. Увы обо всех я не знаю.
1
 Аватар для Enifan
1845 / 1187 / 501
Регистрация: 14.10.2018
Сообщений: 3,204
25.08.2020, 12:52
Цитата Сообщение от loyalist28 Посмотреть сообщение
Вот тоже не пойму, разве компилятор не в курсе о иерархии наследования ограничивающего типа? Мы же в коде все это указываем и он знает, какие типы наследуют от ограничивающего типа. Почему он не использует эту информацию во время компиляции применительно к ограниченным обобщенным типам?
Знать то он знает, только во избежания ошибок делает согласно четкой инструкции.
Если брать за основу вышеуказанное наследование where T : A, то обобщение будет работать с классом A. А что касается иерархии - сделайте проще - сперва удалите всю иерархию, потом снова создайте, потом удалите, потом создайте. Будет ли вызываемый метод MethodA() при этом меняться - однозначно нет. В этом и заключается суть строгой типизации, и вы всегда знаете как работает ваш код.
1
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
25.08.2020, 13:02
Цитата Сообщение от loyalist28 Посмотреть сообщение
разве компилятор не в курсе о иерархии наследования ограничивающего типа?
Нет.

Цитата Сообщение от loyalist28 Посмотреть сообщение
Мы же в коде все это указываем
А если не указали в коде?
Не забывайте, что обобщения — это не плюсовые шаблоны, а обобщенные типы, т.е. должы работать в любой ситуации, в том числе когда сконструированный тип создается во время выполнения, о чем компилятор физически знать не может.

Цитата Сообщение от loyalist28 Посмотреть сообщение
Почему он не использует эту информацию во время компиляции применительно к ограниченным обобщенным типам?
Потому что тогда типы перестают быть обобщенными.
0
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
25.08.2020, 17:47  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
Нет.
Как нет то?
Вы хотите сказать, что компилятор не знает о том, что Derived наследует от Base?? А как же мы тогда можем операции приведения типов проводить и при этом компилятор выдает нам ошибку времени компиляции при преобразовании друг к другу не родственных типов? Или пользоваться членами базового класса в экземпляре класса наследника? О_о
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
25.08.2020, 17:47
Помогаю со студенческими работами здесь

Сравнение обобщённых типов
Доброго времени суток. Недавно получил задание: реализовать неубывающую очередь с приоритетами. Реализовал для приоритета (ключа) типа int,...

Динамическое изменение типа объекта (для обобщённых типов)
Здравствуйте, проблема в следующем: есть класс с generic: public class Entity { //создаем объект...

Ключевое слово where, обьявление обобщенных-типов. Можно ли установить несколько условий ?
Доброго времени суток. Вопрос такой: можно ли иметь несколько условий для where при обьявлении обобщенных типов ? Например, что-то вроде...

Инициализация ссылочных типов в структурах\классах
Добрый вечер. Заинтересовал следующий вопрос. Как лучше инициализировать поля класса\структуры(для структуры вроде нельзя инициализировать...

Обработка исключений в классах для пользовательских типов
Здорова господа! Запустил токо класс String тестю и тут у меня вылетело исключение выход за пределы индекса, и программа тупо аварийно...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru