Здравствуйте, Sharov, Вы писали:
S>>>Хочу посмотреть на реализацию тезиса НС>>Newtonsoft.Json/System.Text.Json подойдут? S>Да, было бы классно, если бы прямо в код ткнули.
Кто бы сомневался, что сам ты даже элементарных вещей делать не желаешь. Раз за разом одно и тоже.
Здравствуйте, Sharov, Вы писали:
S>Альтернатива не использовать наследование, о чем и речь ведется.
И какми конкретно инструментами ты собрался это реализовать? От делегатов ты открещиваешься. Свитч — еще медленнее, чем vtable. Ну так что, ты вообще понимаешь, о чем говоришь?
S>Т.е. Вы вперлись в разговор с какой-то хренью, не способны обосновать свои доводы, и просите, чтобы S>другие за Вас обосновали. Успехов.
Про хрень — см выше.
Ад пуст, все бесы здесь.
Re[35]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Зависит от ORM. Большинство поддерживает три стандартных механихма — общая таблица с дискриминантом, отдельная таблица для каждого неабстрактного типа и таблицы-расширения. У каждого есть свои плюсы и минусы. Но не советую использовать ни один из них, наследование уж точно не для реляционной модели.
Ага. Вообще, "наследование" в моделях данных — болезненная тема. В основном потому, что большинство маститых учебников его вовсе игнорируют, в итоге новобранцы неизменно пытаются применить подходы из ООП.
Которое вообще не про структуру, в про поведение.
У нас в рамках курса по базам данных все курсовые проекты полны моделями, в которых много is-a отношений.
И очень прикольно обсуждать со студентами, как подобные вещи выражать в ER-модели или в реляционной модели.
Ну, там, скажем, у станка на производстве есть список допущенных к нему рабочих, при этом у рабочего есть уровень квалификации, и у станка может быть задан минимальный уровень квалификации допущенного к нему работника.
А руководить отделом может только сотрудник с высшим образованием, подтверждённым дипломом.
При этом зарплата начисляется и тем и другим, т.е. зарплатная ведомость содержит ссылки на некоего "абстрактного сотрудника".
Какие из этих ограничений можно выразить в ER-модели? Какие — в реляционной? Какие стоит проверять динамически, в приложении?
И возникают множество всяких забавных вопросов, которые намеренно не оговорены в задании — скажем, может ли сотрудник быть одновременно менеджером и рабочим? Будем ли мы "наследовать" кого-то из них от другого, и если да, то как? И как быть с сотрудником, который начинал идти по рабочей линии, а потом стал менеджером?
На фоне этого вопросы о том, как и от кого наследовать DTO-классы, бледнеют
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[41]: почему в C# до сих пор нет наследования конструкторов?
Тут пишут, что дин. генерация для св-в, конструкторов и полей:
Moving retrieval of type metadata from runtime to compile-time means that there is less work for the serializer to do on start-up, which leads to a reduction in the amount of time it takes to perform the first serialization or deserialization of each type.
In previous versions of System.Text.Json, the serializer always used Reflection.Emit where possible to generate fast member accessors to constructors, properties, and fields. Generating these IL methods takes a non-trivial amount of time, but also consumes private memory. With source generators, we are able to generate code that that statically invokes these accessors. This eliminates time and allocation cost due to reflection.
Какая связь с вирт. методами и их оптимизацией путем генерации кода?
Кодом людям нужно помогать!
Re[28]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Ночной Смотрящий, Вы писали:
S>>И почему нельзя от какого-нибудь обобщенного Middleware унаследоваться? НС>Можно пример?
Не смогу, поскольку не до конца понимаю, что надо. Может выбрали интерфейс, т.к. по дизайну было лучше.
А не из-за производительности.
S>>И что, в плане UI что изменилось? НС>Изменилось. Десктопный UI сдох вместе с десктопом, а в вебе все несколько иначе, хотя попытки натянуть ОО были и будут.
Ну учитывая, что в js у нас prototype based наследование, едва ли они ушли от наследования per se. Т.е.
остутсвие классов не явл. проблемой для наследования в дин. языкых. Идея переиспользовать код она не толька для
чистых ОО актуальна и реализуема.
S>>А в чем будет усложнение? НС>В развесистых иерархиях наследования, накладывающих очень жесткие ограничения на дизайн в силу высокой связности.
Ну если у нас грид, то как textbox его странно использовать. Хотя можно как-то извратиться, наверное.
Или кнопка как tb.
S>> но все равно фреймворк работает с объектами определенного типа. НС>Какой фреймворк? О чем ты?
UI фреймворк, очевидно, типа WinForms. Почему-то все формы надо от Form наследовать.
Кодом людям нужно помогать!
Re[29]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Sharov, Вы писали:
S>>>И почему нельзя от какого-нибудь обобщенного Middleware унаследоваться? НС>>Можно пример? S>Не смогу, поскольку не до конца понимаю, что надо.
S>>> но все равно фреймворк работает с объектами определенного типа. НС>>Какой фреймворк? О чем ты? S>UI фреймворк, очевидно, типа WinForms. Почему-то все формы надо от Form наследовать.
И что в этом хорошего? Почему в ASP.NET не надо контроллеры от одного объекта наследовать?
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[29]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Codealot, Вы писали:
НС>>Там где перф настолько критичен в дотнете их заменяют динамической кодогенерацией чаще всего. C>Это разве как-то отменяет необходимость в полиморфизме?
ВИртуальные методы — не единственный способ полиморфизма. И да, в ряде случаев отменяют. ПОтому что то, что может быть полиморфно на этапе компиляции исходников не обязательно полиморфно на этапе динамической генерации. К примеру, код Serialize(object obj) полиморфен, потому что obj может быть любого типа, а вот если там внутри написано что то вроде GenerateSerializer(obj.GetType()), то полиморфизм в сгенерированном коде уже не нужен.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[42]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Sharov, Вы писали:
S> generator.Emit(OpCodes.Callvirt, realMethod);
Callvirt может использоваться для вызова невиртуальных методов, ровно как Call — для вызова виртуальных. Callvirt в данном контексте более универсален, поэтому его и используют. А реальный вызов, виртуальный или нет, генерирует JIT. И если в аргументе будет невиртуальный метод — никакого виртуального вызова не будет.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[29]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Sharov, Вы писали:
S>Почему? Из-за избыточности и сложности?
В основном — потому, что оно плохо совместимо с реальной архитектурой.
Единственное, для чего подходит change tracking — это такой нишевой случай "что вижу, то меняю".
Например, редактирование справочников. Фактически, редактор справочника в веб-приложении недалеко ушёл от той самой настольной программы, где на экран показываются почти что байты из файла данных, и пользователь их редактирует. Потом нажимает save, и обновлённые байты уезжают на диск.
Эта концепция опасна, в первую очередь, своей привлекательностью.
Разработчику, травмированному этой моделью, кажется, что любую бизнес-операцию можно реализовать поверх неё.
Ну, например — перевод денег внутри банка с моего счёта на счёт пользователя X.
Почему бы не представить это как две операции редактирования: уменьшаем остаток на моём счету, увеличиваем остаток на счету пользователя X?
Пока приложение бегало на локальной машинке поверх DBF файлов, это был совершенно нормальный способ работы.
А вот в современных крупномасштабных приложениях это начинает работать очень плохо.
В основном потому, что в change tracking у нас нет операции "уменьшить". У нас есть операции "загрузить в память" и "записать в СУБД".
В итоге вместо простого update accounts set balance = balance — @amount where id = @sourceAccount мы делаем сначала select * from accounts where id = @ID, а потом update accounts set balance = @balance where id = @id. То есть вместо 1 раундтрипа мы получили 2 раундтрипа.
При этом от времени между первым и вторым у нас ещё и зависит шанс нарваться на несогласованность — если мы делаем пессимистичную блокировку, то нам нужно не забыть об этом при селекте: select * from accounts where id = @ID with (holdlock), ну, или вообще все эти приплясывания выполнять в рамках транзакции с уровнем изоляции repeatable read.
Ну, или сделать optimistic locking и рисковать откатом транзакции потому, что кто-то вклинился между вызовами select и update.
В обоих вариантах мы затягиваем транзакцию, а это резко уменьшает возможное количество запросов, одновременно обслуживаемых СУБД.
Получается парадокс — мы уходим от, к примеру, хранимых процедур, чтобы разгрузить СУБД и перенести часть логики на легко масштабируемый слой сервера приложений, а нагрузка на СУБД только возросла .
Но это мы рассмотрели крошечный пример, с одним объектом простой структуры. В более сложных сценариях типа "зарезервировать товар на нескольких складах" у нас вот это вот общение между апп-сервером и СУБД сводится к затаскиванию в RAM половины базы и медленному неэффективному обходу получившегося графа объектов.
А потом мы ещё и пихаем в СУБД пачку update, по которым решительно непонятно, от каких же из сделанных ранее селектов они зависят.
Ну, и вишенка на торте — ничего этого не видно со стороны кода приложения. Там — сплошное благолепие, обращение к репозиториям, циклы-модификации. Понять, что происходит какой-то трэш, можно только подсмотрев в трафик СУБД при помощи какого-нибудь SQL Profiler.
В общем, такой код легко писать, но трудно тестировать/отлаживать, и невозможно исполнять эффективно.
Вообще, в этом же форуме было уже несколько горячих баталий по теме ORM. Там всё это было неоднократно высказано.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[37]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>ВИртуальные методы — не единственный способ полиморфизма.
Предлагай альтернативы. Кроме очевидных, которые работают на такой же скорости или медленнее.
НС>И да, в ряде случаев отменяют.
А в ряде случаев — нет.
НС>К примеру, код Serialize(object obj) полиморфен, потому что obj может быть любого типа, а вот если там внутри написано что то вроде GenerateSerializer(obj.GetType())
Это не полиморфизм, это reflection.
Ад пуст, все бесы здесь.
Re[38]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Codealot, Вы писали:
C>Предлагай альтернативы. Кроме очевидных, которые работают на такой же скорости или медленнее.
Стесняюсь спросить: для какой задачи?
Идея обсуждать решения в отрыве от задачи лично мне видится в высшей степени странной.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[39]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Sinclair, Вы писали:
S>Стесняюсь спросить: для какой задачи? S>Идея обсуждать решения в отрыве от задачи лично мне видится в высшей степени странной.
Для любой, где используются витртуальные методы. Хотя бы один пример.
Ад пуст, все бесы здесь.
Re[38]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Codealot, Вы писали:
НС>>ВИртуальные методы — не единственный способ полиморфизма. C>Предлагай альтернативы. Кроме очевидных, которые работают на такой же скорости или медленнее.
Один я уже предложил — динамическая кодогенерация.
НС>>К примеру, код Serialize(object obj) полиморфен, потому что obj может быть любого типа, а вот если там внутри написано что то вроде GenerateSerializer(obj.GetType()) C>Это не полиморфизм, это reflection.
Это самый натуральный полиморфизм, обеспечивающий полиморфное поведение и скорость равносильную статическим вызовам. Есть еще статический полиморфизм, когда полный набор форм известене на этапе компиляции. В дотнете такое можно изобразить при помощи перегрузки функций или операторов. Есть еще параметрический полиморфизм, в дотнете представленный дженериками. Нам его базе есть интересные фокусы с использованием того факта, что статические поля дженерик класса для каждого дженерик параметра свои. Наконец, в свеженьком шарпе есть static virtual, который в рантайме не порождает виртуального вызова с адресацией через vtbl, а так же опирается на кодогенерацию дженериков JITом.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[36]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Codealot, Вы писали: C>Свитч — еще медленнее, чем vtable.
// * Summary *
BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19045.2251)
Intel Core i7-8565U CPU 1.80GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical cores
.NET SDK=6.0.401
[Host] : .NET 6.0.9 (6.0.922.41905), X64 RyuJIT AVX2
DefaultJob : .NET 6.0.9 (6.0.922.41905), X64 RyuJIT AVX2
| Method | Mean | Error | StdDev |
|-------- |---------:|--------:|--------:|
| Virtual | 189.0 us | 3.77 us | 4.03 us |
| Switch | 178.2 us | 2.98 us | 3.19 us |
Код теста
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run<SwitchVsVirtual>();
public class SwitchVsVirtual
{
private readonly Base[] _vData;
private readonly SwitchData[] _sData;
public SwitchVsVirtual()
{
var rnd = new Random(Environment.TickCount);
var randoms = Enumerable.Range(0, 10000).Select(_ => rnd.Next(0, 3)).ToArray();
_vData = randoms
.Select(r => r switch {
0 => (Base)new DerivedA(),
1 => new DerivedB(),
2 => new DerivedC(),
_ => throw new ArgumentOutOfRangeException()
})
.ToArray();
_sData = randoms
.Select(r => new SwitchData(r switch {
0 => Discriminant.A,
1 => Discriminant.B,
2 => Discriminant.C,
_ => throw new ArgumentOutOfRangeException()
}))
.ToArray();
}
[Benchmark]
public int Virtual()
{
var res = 0;
foreach (var data in _vData)
res += data.Data.Length;
return res;
}
[Benchmark]
public int Switch()
{
var res = 0;
foreach (var d in _sData)
res += d.Data.Length;
return res;
}
}
abstract class Base
{
public abstract string Data { get; }
}
class DerivedA : Base
{
public override string Data => "A";
}
class DerivedB : Base
{
public override string Data => "B";
}
class DerivedC : Base
{
public override string Data => "C";
}
enum Discriminant { A, B, C }
record SwitchData(Discriminant Discriminant)
{
public string Data =>
Discriminant switch
{
Discriminant.A => "A",
Discriminant.B => "B",
Discriminant.C => "C",
_ => throw new ArgumentOutOfRangeException()
};
}
C> Ну так что, ты вообще понимаешь, о чем говоришь?