Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.71/14: Рейтинг темы: голосов - 14, средняя оценка - 4.71
 Аватар для Enifan
1848 / 1190 / 501
Регистрация: 14.10.2018
Сообщений: 3,211

Перегрузка оператора без создания нового объекта

27.02.2020, 21:59. Показов 2857. Ответов 9

Студворк — интернет-сервис помощи студентам
Если вкратце то вопросы звучат так: Можно ли на практике делать перегрузку оператора без создания нового объекта и какие баги (или нечто подобное) мы можем словить?
Создавать или не создавать объект?

Рассмотрим на простом примере (пока что без перегрузок)
C#
1
int num = 1 + 2;
Если бы мы создавали новый объект, то сначала создаем новое число из 1 + 2, потом это значение записываем в переменную
Если мы мы не создаем новый объект, то изменяем одно из значений (пусть это будет 1), оно становится равно 3 (не к добру это) и после этого записываем в переменную.

Теперь рассмотрим на более реальном примере.
Создание нового объекта
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyClass
{
    public int X;
 
    public static MyClass operator +(MyClass op, int number)
    {
        return new MyClass { X = op.X + number };
    }
}
 
class Program
{
    static void Main()
    {
        MyClass my = new MyClass() { X = 5 };
        System.Console.WriteLine((my += 1).X);
    }
}
Плюсы: если созданим новую ссылку на объект MyClass m2 = my + 1; то первый объект не изменится
Минусы: приходится создавать новый объект, а так же копировать/создавать новые данные. При большом кол-ве данных это может быть затратно. Конструктор копирования поможет упростить код, но не время работы.

Та же версия, только Без создания нового объекта
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyClass
{
    public int X;
 
    public static MyClass operator +(MyClass op, int number)
    {
        op.X += number;
        return op;
    }
}
 
class Program
{
    static void Main()
    {
        MyClass my = new MyClass() { X = 5 };
        MyClass my2 = my + 1;
        System.Console.WriteLine(my.X + " " + my2.X);
    }
}
Плюсы: чтобы изменить 1-ый объект не обязательно писать my += 1;, а можно просто my + 1;. Сокращенная запись (хотя не совсем логичная с точки зрения обычного синтаксиса C#), не надо присваивать ссылку на себя. Никаких копирований данных всего объекта. Изменили одно значение и точка.
Минусы: даже если мы не хотим менять 1-ый объект, он все равно изменится (что есть очень плохо).

А если работать с Синглтоном? Новый объект создать не сможем, как следствие перегрузка с создание нового объекта невозможна.

Вывод: при работе с одним объектом (например в Main()) эффективнее работать без создания нового объекта. В остальных случаях - создаем новый. Какие советы можете дать по этому поводу и почему?
Баги и прочее без создания нового объекта

При инкременте ++ и декременте -- без создания нового объекта у нас всегда срабатывает только префиксная форма, постфиксная просто невозможна.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyClass
{
    public int X;
 
    public static MyClass operator ++(MyClass op)
    {
        op.X += 1;
        return op;
    }
}
 
class Program
{
    static void Main()
    {
        MyClass my = new MyClass() { X = 5 };
        System.Console.WriteLine((my++).X);
    }
}
Какие еще баги/прочее существуют?
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
27.02.2020, 21:59
Ответы с готовыми решениями:

Перегрузка оператора + и создание нового объекта на этой основе
Такая проблема. Реализую класс строка. Весь нужный код ниже. Вот так работает: STRING str1("123"); STRING...

Передача существующего объекта вместо создания нового
Есть класс Word. Он неизменяемый: все методы - константные. Учитывая это я пытаюсь создать класс таким образом чтобы вместо создания...

Восстановление профиля без создания нового
Возникла такая проблема, не работает кнопка пуск, на панели задач не отображаются 2 иконки: магазина и edge, стояла windows 10 pro, на...

9
 Аватар для QuakerRUS
1469 / 1010 / 456
Регистрация: 30.10.2017
Сообщений: 2,799
27.02.2020, 22:44
Цитата Сообщение от Enifan Посмотреть сообщение
Плюсы: чтобы изменить 1-ый объект не обязательно писать my += 1;, а можно просто my + 1;
Не назвал бы это плюсом. Как минимум мне кажется это нелогичным. Для изменения лучше использовать +=, который автоматически перегружается при определении оператора +.

Добавлено через 3 минуты
Цитата Сообщение от Enifan Посмотреть сообщение
А если работать с Синглтоном? Новый объект создать не сможем, как следствие перегрузка с создание нового объекта невозможна.
Можно сделать обертку и перегружать операции для нее.

Добавлено через 2 минуты
Цитата Сообщение от Enifan Посмотреть сообщение
Минусы: приходится создавать новый объект, а так же копировать/создавать новые данные. При большом кол-ве данных это может быть затратно.
Опять-таки можно работать с оберткой и копировать минимум данных.

Итого вариант с созданием нового объекта мне кажется наиболее логичным и охватывает максимум стандартных возможностей. Но думаю надо отталкиваться от конкретной задачи и выбирать более подходящий вариант в каждом конкретном случае.
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,080
Записей в блоге: 2
28.02.2020, 00:24
Лучший ответ Сообщение было отмечено Enifan как решение

Решение

Цитата Сообщение от Enifan Посмотреть сообщение
Можно ли на практике делать перегрузку оператора без создания нового объекта и какие баги (или нечто подобное) мы можем словить?
Создавать или не создавать объект?
Можно для ссылочных типов, но не нужно.
В настоящее время прозрачность, читаемость кода считаются основными его характеристиками.
В C# принято, что операторы создают новый объект.
Если поведение будет другое, то затруднит понимание кода и увеличит вероятность ошибок.

Операторы по сути это обычные статические методы.
Нужен вам метод не создающий новый объект, так и сделайте такой метод.
Ни какой необходимости возлагать это не оператор нет.
В типовой реализации это (метод не создающий новый экземпляр, а изменяющий нужный) должен быть не член типа, а член экземпляра.
Это облегчит понимание вашего кода.
1
 Аватар для Enifan
1848 / 1190 / 501
Регистрация: 14.10.2018
Сообщений: 3,211
28.02.2020, 17:24  [ТС]
QuakerRUS, Элд Хасп, Если брать в целом, то я согласен что перегрузка через создание нового объекта будет правильней, и этим способом я обычно и пользуюсь. Просто было любопытно - есть ли моменты, где альтернативный способ может быть лучше (не в теории, а на практике).
Цитата Сообщение от QuakerRUS Посмотреть сообщение
Можно сделать обертку и перегружать операции для нее.
Цитата Сообщение от QuakerRUS Посмотреть сообщение
Опять-таки можно работать с оберткой и копировать минимум данных.
Я с обертками можно сказать не сталкивался, тем более никогда не перегружал. Можно посмотреть хоть на какой нибудь простой пример как это будет выглядеть? Интересен как синглтон, так и копирование минимум данных.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,080
Записей в блоге: 2
28.02.2020, 17:59
Цитата Сообщение от Enifan Посмотреть сообщение
Интересен как синглтон,
Для Singleton зачем это может понадобиться?
Весь смысл его создания - это использование единственного экземпляра.
И все методы работаю с этим экземпляром.
Статические же методы (в том числе операторы) нужны когда возникает необходимость в обработке РАЗНЫХ экземпляров типа.

То есть сделать-то можно, но тогда это будет уже не Singleton.

Добавлено через 5 минут
Цитата Сообщение от Enifan Посмотреть сообщение
Можно посмотреть хоть на какой нибудь простой пример как это будет выглядеть? Интересен как синглтон, так и копирование минимум данных.
Оболочка для Листа с переопределённым плюсом
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    public class ListAdd<T> : List<T>
    {
        public static ListAdd<T> operator +(ListAdd<T> left, T right)
        {
            left.Add(right);
            return left;
        }
        public static ListAdd<T> operator +(T left, ListAdd<T> right)
        {
            right.Insert(0, left);
            return right;
        }
        public static ListAdd<T> operator +(ListAdd<T> left, ListAdd<T> right)
        {
            left.AddRange(right);
            return left;
        }
    }
1
 Аватар для QuakerRUS
1469 / 1010 / 456
Регистрация: 30.10.2017
Сообщений: 2,799
28.02.2020, 18:05
Лучший ответ Сообщение было отмечено Enifan как решение

Решение

Цитата Сообщение от Enifan Посмотреть сообщение
Я с обертками можно сказать не сталкивался, тем более никогда не перегружал. Можно посмотреть хоть на какой нибудь простой пример как это будет выглядеть? Интересен как синглтон, так и копирование минимум данных.
Я с ним сам не имел опыта работы, но взял пример отсюда.
https://metanit.com/sharp/patterns/2.3.php

Дорисовал обертку в виде OSWrapper, в котором перегрузил операцию ++. Можно спокойно создавать новый объект OSWrapper, это будет ненакладно. В нем хранится ссылка на текущий синглтон. Может где то неправильно или неоптимально сделал, от паттернов проектирования я пока далек. Новый строки 15-18, 49, 52-68 (если ничего не забыл).

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
using System;
 
class Program
{
    static void Main(string[] args)
    {
        Computer comp = new Computer();
        comp.Launch("Windows 8.1");
        Console.WriteLine(comp.OS.Name);
 
        // у нас не получится изменить ОС, так как объект уже создан    
        comp.OS = OS.getInstance("Windows 10");
        Console.WriteLine(comp.OS.Name);
 
        OSWrapper comp2 = new OSWrapper(OS.getInstance("123"));
        Console.WriteLine(comp2);
        comp2++;
        Console.WriteLine(comp2);
 
        Console.ReadLine();
    }
}
class Computer
{
    public OS OS { get; set; }
    public void Launch(string osName)
    {
        OS = OS.getInstance(osName);
    }
}
class OS
{
    private static OS instance;
 
    public string Name { get; private set; }
 
    protected OS(string name)
    {
        this.Name = name;
    }
 
    public static OS getInstance(string name)
    {
        if (instance == null)
            instance = new OS(name);
        return instance;
    }
 
    public void Increase() { Name = "New OS"; }
}
 
class OSWrapper
{
    public OS Os { get; set; }
    public OSWrapper (OS os) { Os = os; }
 
    public static OSWrapper operator ++(OSWrapper os)
    {
        OSWrapper result = new OSWrapper(os.Os);
        result.Os.Increase();
        return result;
    }
 
    public override string ToString()
    {
        return Os.Name;
    }
}
1
 Аватар для Enifan
1848 / 1190 / 501
Регистрация: 14.10.2018
Сообщений: 3,211
28.02.2020, 18:17  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Оболочка для Листа с переопределённым плюсом
В целом пример простой и наглядный, спасибо. Но в данных перегрузках нет создания нового объекта. Это специально было сделано, дабы упростить код, не писав каждый раз new ?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,080
Записей в блоге: 2
28.02.2020, 18:28
Цитата Сообщение от Enifan Посмотреть сообщение
В целом пример простой и наглядный, спасибо. Но в данных перегрузках нет создания нового объекта. Это специально было сделано, дабы упростить код, не писав каждый раз new ?
Это демонстрация, что без создания нового экземпляра смысл операторов исключается.

Да и в целом, для ссылочных типов применение операторов та ещё идея...
Возможно, но зачем?

Какой-то смысл имеет переопределение == и != для неизменяемых DTO типов.
Ещё гораздо реже встречается переопределение операторов сравнения.

А вот арифметические операторы.... На то они и арифметические, чтобы работать с числами.
А числа это ValueType. И по самой сути числа должны быть неизменяемыми.
То есть опять возвращаемся к тому же: новое значение - новый тип.

То есть C# позволяет натворить очень много разного кода, но для того чтобы этот код считался хорошим надо соблюдать принятые приёмы, соглашения, рекомендации.
В том числе оставить Арифметические операторы для Арифметики.
1
 Аватар для Enifan
1848 / 1190 / 501
Регистрация: 14.10.2018
Сообщений: 3,211
29.02.2020, 18:11  [ТС]
QuakerRUS, основную идею понял, спасибо.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
для ссылочных типов применение операторов та ещё идея...
возможно, но класс string видимо исключение.

В итоге усвоил тот факт, что проще создать объект, и знать что никаких ошибок потом ожидать не надо. В крайней случаи проще создать метод. Возможно найдется тот пример где можно будет обойтись без создания нового объекта, но это сомнительно.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,080
Записей в блоге: 2
29.02.2020, 18:54
Цитата Сообщение от Enifan Посмотреть сообщение
возможно, но класс string видимо исключение.
Это "не из той оперы".
"string" неизменяемый тип. И любой метод для него это обёртка над статическим методом который возвращает новый экземпляр "string".
И, в какой-то , мере это действительно исключение возникшее на базе ранних ЯП где было "суммирование" строк.
Создавайся C# на пустом месте, с чистого листа, очень сомневаюсь чтобы у "string" был бы оператор "+".

Если мылить абстрактно, когда есть "+", то должен быть и "-".
И не зная как определить "-" - не надо определять и "+".

Например, для событий есть и "+=" и "-=".
Они определены оба и дополняют друг друга.
Поэтому для событий это смотрится логично, хоть это и ссылочный тип.

Добавлено через 8 минут
Допустим, если бы можно было перегружать "+=" без перегрузки "+", то можно было дополнить List соответствующими операторами.
C#
1
2
3
4
List<T> list = new List<T>();
T t =  new T();
list += t;
list -= t;
Но так нельзя.
А конструкция с отдельными операторами "+" и "-" смотрится очень ущербно и непонятно.
C#
1
2
list = list + t;
list = list - t;
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
29.02.2020, 18:54
Помогаю со студенческими работами здесь

Перевернуть массив без создания нового.
static int massback() { int inputmas=inputmass(); // inputmass(); - функция для ввода массива. int backmass = new int ; ...

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

Использование WinApi в C#. Как изменить шрифт без создания нового?
Пытаюсь установить шрифт через Структуру LOGFONT: LOGFONT logFont= new LOGFONT(); logFont.lfHeight=30; ... IntPtr pFont =...

Можно ли создать форму в модуле без создания нового модуля для неё
Можно ли создать форму в модуле без создания нового модуля для неё?

Создание объекта без создания класса
Например мне нужно создать объект, который имеет параметр String, параметр Bool и параметр Int. Из этих объектов мне надо создать список....


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

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru