Pull to refresh
18
0
Павел @INC_R

C# developer

Send message

Не очень понял один момент из статьи. Подскажите, вы с клиента обращаетесь по API, или парсите страницы?

Это зависит, мне кажется. Например, если обращение к пользователю по имени различается в зависимости от каких-то факторов (скажем, до 18 лет — ФИ, 18+ — ФИО) — это может и с натяжкой, но бизнес-логика.


И мне лично в таких случаях было бы комфортнее писать person.Name.OurSpeciallyFormattedName, а не new PersonNameFormatter().FormatOurSpeciallyFormattedName(person).
Сущность Person это не особо забивает, потому что из нее торчит не простыня форматов имени на все случаи жизни, а только что-то вроде public PersonName Name => new PersonName(this), и именно этот PersonName инкапсулирует всю логику, связанную с разными форматами имени. Вроде как такой торчащий из сущности PersonName — это пример Value Object, хотя тут я могу заблуждаться.


На истину не претендую; если такой подход чем-то плох, рад был бы услышать критику.

Переменные есть в другой статье, про локальные переменные.


У вас IForecast any; — это что? Локальная переменная или поле? Приведите полный кусок кода, без пропусков.
Из текста следует, что это локальная переменная. Если вы подразумевали, что это поле, то ладно, суди это не меняет: объявляется поле класса, создается экземпляр класса.


И не увлекайтесь сильно русским мсдн-ом, там часто машинный перевод, и он откровенно кривой.


но и не переменной, а поля. Под которым в дальнейшем подразумевается класс.

Простите, что? Под полем подразумевается класс?


Я не понимаю, зачем это все здесь обсуждать. Очевидно у вас тоже не уверенные знания.
Затем, что ваша обучающая статья сдержит ошибки и обучаться кому-то по ней противопоказано.
Ок, у вас в проектах все без исключения классы имеют интерфейсы потому что ООП? И еще наверно абстрактный класс до кучи?

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


Я уже ниже писал, что если погуглить англоязычные форумы, то там пишут, что интерфейсы не надо липить везде подряд вот или вот

Вам здесь еще никто и не сказал, что их надо лепить везде подряд.

Если верить МСДНу, то создают экземпляр класса, а не «переменную типа». Да, не объявляют. 1:1

О, вы счет ведете? Круто. Так вот:
Экземпляр класса создают. Переменную типа объявляют. Это разные вещи.
А у вас в статье "объявляют экземпляр класса":


В такой случае можно объявить экземпляр класса, реализующего интерфейс:

IForecast any;
Что тут не понятно? Три класса имеют одинаковые методы. Было бы красиво прикрутить интерфейс, который они реализую.

Да, пожалуй, мне стоило назвать классы чуть иначе. Например, FooTaskRunner, BarTaskRunner и BazTaskRunner. Было бы чуть очевиднее.
Понимаете, было бы не "красиво" прикрутить интерфейс. Это необходимо, потому что ООП и далее по тексту.


Ваша логика примерно такая: можно не делать интерфейсы, если можно написать без них. Это примерно тот же уровень логики, как "можно не писать грамотно, мы не в школе". Что угодно можно написать без интерфейсов. Но это не значит, что это нужно делать.

Я вам по существу отвечу про hello world здесь, ладно? А то вы много раз про это напоминали. Обрадую: к hello world интерфейс не нужен. Но это несостоятельный пример ровно по той причине, что hello world вы пишете примерно один раз в жизни.


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


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


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


У вас вроде обучающая статья. Вы ориентируетесь на тех, кто только учится, у кого нет знания терминологии. И у вас в статье с терминологией просто ужас. Интерфейсы не наследуются, а реализуются. Объявляют не "экземпляр класса", а "переменную типа". Классы с общими признаками, и возможность их использовать, не зная конкретного типа, называется полиморфизмом. Такие ошибки просто недопустимо делать, если вы хотите чему-то кого-то научить.

Мне этого рассказывать не надо, я про все это знаю. Я бы на ревью этот код завернул. И в собственных проектах тоже без весомой причины такое никогда бы не написал.


Вы уж определитесь, "интерфейс просится" или "не изменится и лучше не станет"?

Вот это все работает без интерфейсов
public class A {
    public void Run() => Console.WriteLine("A");
}

public class B {
    public void Run() => Console.WriteLine("B");
}

public class C {
    public void Run() => Console.WriteLine("C");
}

object GetSmth() => new B();

void Main()
{
    var smth = GetSmth();

    if (smth is A a)
        a.Run();

    if (smth is B b)
        b.Run();

    if (smth is C c)
        c.Run();
}

Тут ничего не изменится и не улучшится с добавлением интерфейса?

Тогда бы стоило написать, зачем они действительно нужны. То есть SOLID, DI, IOC, полиморфизм и вот это все.


А у вас "можно методы с одинаковыми именами сделать, если никак не придумать уникальное". Статья наполовину про то, что "по назначению использовать интерфейсы вам пока не надо, поэтому можно вот такую штуку сделать, потому что можем".

Разве? А msdn вот более строг: Represents a wrapper class for operating system handles. Как я понял из документации, там какая-то специфичная для дескрипторов ОС магия происходит, чтобы ресурс не поломался. Но могу ошибаться, конечно.

Но тут не сказано, что это обязательно будет сделано ) Сказано, что это "помогает собрать сразу", а не "сразу приводит к сборке". Хотя в оригинале более точная формулировка, перевод искажает смысл:


The JIT and the GC are working together to track some auxiliary information that helps the GC to clean objects as soon as possible.

А разве SafeHandle используется не исключительно для управления дескрипторами операционной системы? Если нужно работать со сторонними неуправляемыми ресурсами, то он же не подойдет? А то я на практике в этой "неуправляемой" области не работал, не особо ориентируюсь.
И как я понимаю, сам SafeHandle использует финализатор для своей работы. Так что в конечном итоге финализаторы нужны, чтобы сделать вещи вроде SafeHandle, которые позволяют не писать финализаторы самому )

Финализаторы нужны, если вы работаете с неуправляемыми ресурсами (что само по себе крайне редкое явление, конечно). Например, выделяете неуправляемую память. Тогда наличие правильного финализатора гарантирует, что если вы сами не освободите такие ресурсы, потому что забыли using или что-то еще навернулось, то в итоге их освободит финализатор и утечек не будет.

вот как раз в документации то, ничего про callback не сказано.
А в документации которую вы показали рассказывается о некой «магии», и ни слова о том, как это действительно проиходит. Потому вы или покажите офф. документацию где явно сказано, что там действительно последующая часть кода выносится в call-back-функцию, или признайте что вы «не совсем правы». Потому что повторюсь — в документации рассказывается о неких «contonuation» — а что это — нигде не написано.

Запросто: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern
Under the covers, the await functionality installs a callback on the task by using a continuation. This callback resumes the asynchronous method at the point of suspension.


И еще: https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/chaining-tasks-by-using-continuation-tasks


In asynchronous programming, it is very common for one asynchronous operation, on completion, to invoke a second operation and pass data to it. Traditionally, this has been done by using callback methods. In the Task Parallel Library, the same functionality is provided by continuation tasks. A continuation task (also known just as a continuation) is an asynchronous task that is invoked by another task, which is known as the antecedent, when the antecedent finishes.


Но код последователен, синхронен, событий и обработчиков в нем не объявлено. Значит это синхронный код.

Где вы вообще взяли термин "синхронный код" и что это значит? Какая разница, написано оно текстом через события и обработчики или последовательно, если результат одинаковый? Синхронность/асинхронность — свойство выполняемых операций, а не текста, их представляющего. Мне без разницы, как писать код: через await или на callback-ах, результат будет одинаковый. Разве что на await это читаемо, а на callbackах мозг можно сломать.


Классификация исходного кода изменится или нет? можете ответить на этот вопрос?

Опять же. Я не понимаю, где вы взяли термин "синхронный исходный код" в вашей интерпретации. Приведите, пожалуйста, авторитетный источник.
Но если говорить об этом в ваших терминах, то нет, если в результате поведение не отличается от исходного, то "синхонность" не изменится.

Когда у вас вычисления внутри вызываемой через await функции — они выполняется внутри какого-то потока.

Вычисления — это что? Вся полезная нагрузка или именно обычные вычисления на CPU? Если вся полезная нагрузка, то вы ошибаетесь, потому что полезная нагрузка может выполняться в том числе на DMA и подобных механизмах. Если вы только про вычисления на CPU вроде решения всякого матана — ну да, это в конечном итоге делает какой-то поток, и что? Основной профит от асинхронности идет за счет асинхронного IO, который процессор как раз вообще не занимает и никаких потоков там нет.


и выполняемое операционкой действия глубоко внутри — тоже формально прикреплены к какому-либо потоку

Потоки в ОС появляются на определенномуровне абстракции. На уровнях ниже есть драйверы, прерывания, DPC, которые работают "просто на процессоре" и никаких потоков там нет.

не верно, потому что await не регистриурет никаких действий, и его поведение, на уровне исполнния последовательности строчек кода, ничем не отличается от работы семафора.
да, await волшебным образом не блокирует текущий поток, но это иные свойства, не связанные event-driven стилем разработки.

Во-первых, принципиальное отличие await от семафора — отсутствие блокировки. Во-вторых, "волшебным образом" он это делает потому, что это по сути и есть callback, о чем вам сто раз сказали. Но вы, видимо, знаете, как работает await, лучше, чем официальная документация и целая куча специалистов и экспертов.


действия выполняющиеся вне какого-либо потока — нонсенс, противоречит основам работы операционной системы

Вам бы Таненбаума почитать или Руссиновича. Некоторые действия выполняются ОС на таком уровне, где понятия потоков и процессов нет вообще. Это к слову. И есть, например, такая штука, как DMA, которой программно ставится задание что-то где-то переместить в памяти, и она потом это делает сама независимо от процессора, который в это время может делать что угодно другое. Каноничный пример асинхронной операции, для выполнения которой не требуется никакого потока, о чем и была речь в статье.

Предельно очевидно. "Отрицание drive.WriteBytes()" Спасибо, заверните мне await.

Вот и возвращайте ему актуальное значение счётчика

А если у меня нет такого метода? Если у меня есть только два метода: "получить текущее" и "увеличить на 1"?


Слушайте, я не буду изобретать наглядный логичный согласованный пример, когда это может понадобиться, чтобы покрыть все ваши возможные "а что, если". Вот голые требования. Операция Б должна быть выполнена строго после того, как окончательно и успешно выполнилась Операция А.


Если Операция А не дает мне понять, когда она там завершилась и завершилась ли вообще (ведь, по-вашему, мне не должно быть важно, как там внутри работает и когда), то что делать?


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

На текущем уровне абстракции мой язык дает мне полное понимание того, какие гарантии у меня есть. Ваш пример — нет.

Это же частности. Я бы поставил 1000 тасок, а там планировщик сам разберется, как их раскидать по потокам, создать ли 10, 20, 50 потоков или вообще все дать в одном. Это зависит в том числе от железа. На 1-ядерном ПК нет смысла заводить 20 потоков.
И в конце-концов неважно, один поток будет делать 1000 запросов к сервисам или несколько, если время на инициализацию асинхронного запроса заведомо меньше, чем время ответа сервиса.

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Date of birth
Registered
Activity