Начни использовать C# в ZennoPoster уже сегодня - полный курс

volody00

Client
Регистрация
06.09.2016
Сообщения
929
Благодарностей
1 018
Баллы
93
128229


#1
Итак, сначала небольшое предисловие (оно же предостережение).

Я не программист. Это значит, что некоторые вещи я могу объяснять неправильно, могу упускать какие-то важные моменты, могу не оптимально писать код, могу даже неверно использовать терминологию. Это не профессиональный курс.

Я уже несколько лет использую c# в ZennoPoster. Моя основная задача – это максимально простое объяснение материала. Мне нужно, чтобы вы поняли C# и начали использовать в своих шаблонах, а дальше уже при желании можете проходить уроки программистов (на том же ютубе или metanit), если вам это нужно.

И ещё момент – я буду работать в обычном кубике C#, а не Visual Studio. Дело в том, что я не программист и потому никогда особо не пользовался VS. Раньше его не было, и я привык работать так.

Есть несколько причин, по которым я не пытаюсь перейти на VS:
  • Если вы работаете в VS, то вы весь проект будете писать на C#. Тут нужно учитывать, что далеко не все кубики имеют аналог на C#. Кроме того, некоторые вещи банально удобнее делать на кубиках, плюс вы первое время в любом случае будете совмещать у себя и то и другое.
  • В VS есть свои баги, которые надо уметь отлавливать (я имею ввиду в связке с ZennoPoster). Возможно сейчас это уже всё пофиксили, но раньше они точно были. Даже те, кто юзает VS, чаще всего пишут проект там, а потом переносят всё равно переносят в общий код
  • Лично мне неудобно тестировать проект. Мне не нравится, как они вставили браузерное окно в VS.
Из плюсов VS – там лучше работает отладчик кода, плюс сам код набирается быстрее, чем в кубике C#. Плюс там гораздо удобнее выстраивать структуру проекта (особенно если он будет большим). Если вы уверены, что сможете написать весь код на C#, плюс сам шаблон будет большим, то делайте выбор в пользу VS.

То, что я показываю вам в кубике C#, будет работать и VS (главное правильно его подключить - https://zennolab.atlassian.net/wiki/spaces/RU/pages/1375109121/Visual+Studio). Если вам удобнее тренироваться там, я не против.

Классы и объекты

Частенько объясняют так. Класс – это некий чертёж, а объектом будут уже конкретные вещи, которые по этому чертежу создаются.

Возьмём для примера человека. Человек – это класс (class).

У него есть рост, вес, цвет волос, количество пальцев и т.д. Т.е. какое-то описание. Всё это – свойства (properties).

Также человек может ходить, бегать, прыгать, читать, писать и т.д. Т.е. какие-то действия. Это всё – методы (methods).
128218



А вот Вася Пупкин 27 лет или Елена Ивановна 20 лет – это уже объекты, экземпляры класса человек.

Возьмём теперь автомобили. Автомобиль = class. Они у нас имеют цвет, мощность, тип и т.д. Это свойства. Автомобиль может ехать. Это метод. А ВАЗ 2107 будет объектом, экземпляром класса автомобиль.

Поняли?

А теперь перенесёмся в контекст языка C#. Нам с вами доступна большая библиотека классов "из коробки" (Net Framework). Например, класс для работы с датой и временем (DateTime), класс для работы с файлами (FileInfo), класс для работы с папками (DirectoryInfo) и ещё 100500 таких классов. Некоторые из этих классов уже подключены по умолчанию и можно сразу начинать их вызывать. Другие же нужно сначала подключить, но об этом в другой раз.

Кроме того, что доступно "из коробки", у нас есть классы от разработчиков команды ZennoLab. Посмотреть их можно тут - https://help.zennolab.com/en/v7/zennoposter/7.1.4/webframe.html#topic1.html

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

128219


Например, «HtmlElement» нужен для работы с отдельными элементами, которые мы находим на странице. Вы можете узнать ширину и высоту элемента (1), получить его местоположение относительно верхнего края браузера (2), получить имя тэга (3), получить текст (4), проверить существование (5) и т.д. Это всё свойства.
128220


Теперь посмотрим методы. Мы можем кликнуть по элементу(1), перетащить элемент (2), получить его потомков (3) и т.д. Имейте ввиду, что методы (в отличии от свойств) имеют круглые скобки на конце.

128221


Аналогично с другими объектами (классами). Так, «instance» - это браузер. Мы можем получить все вкладки, узнать или установить таймзону, получить или установить куки, установить разрешение и т.д. «tab» - это вкладка браузера. Здесь мы можем находить элементы, двигать мышку, эмулировать клавиатуру.

Помимо "базовой коробки" и "коробки от ZennoLab" мы можем ставить и сторонние библиотеки от какого-нибудь программиста Пети.

Почему я акцентировал на этом внимание. Смотрите, сейчас популярны нейросети, помогающие писать код. Так вот, он хорошо разбирается в "базовой коробке", но очень мало знает о том, что там написали разработчики ZennoLab или какой-то Петя (если только библиотека Пети не стала популярной). Поэтому имейте это ввиду, когда обращаетесь к AI за помощью.

Есть и ещё один момент, о котором стоит упомянуть. Наша "базовая коробка" (уж извините за такой слэнг) всё время пополняется новыми классами, т.е. новым функционалом. Но конкретно в ZennoPoster этого самого обновления не происходит. Мы застряли на .Net Framework (конкретную версию не помню), которая отстаёт от мира на пару лет. Это не страшно, но имейте ввиду, что если ваш код не запускается на ZennoPoster, то или не подключены нужные библиотеки или они в принципе не поддерживаются в ZennoPoster.

На самом деле всё не так страшно, потом это в голове уложится. Всё, что будет даваться в статье, будет работать сразу, так что можете открывать кубик c#, вставлять туда код и смотреть, как это работает.

Основы написания кода

Итак, давайте вникать в тему. Мы с вами знаем, что есть некие классы, у которых есть свойства и методы, позволяющие делать всякие штуки. Т.к. класс это чертеж, то нам нужно сначала создать конкретный экземляр данного класса (объект) и уже него мы просить что-либо сделать.

Давайте потренируемся на Windows Forms. Вы можете использовать формы вместо входных настроек. Также они могут быть полезны, если, например, нужно вручную указать какой-то код для сайта (например, при авторизации в телеграмм или whatsapp).

Есть такой класс как Form, позволяющий работать с формами. Его полное имя System.Windows.Forms.Form. Первым делом создадим по этому чертежу конкретного "Васю Пупкина". Формула тут такая:

Название_Класса любое_имя = new Название_Класса();

System.Windows.Forms.Form form = new System.Windows.Forms.Form();

form – это имя экземпляра класса (объекта). Оно может быть любым (почти). Т.е. вместо form вы можете написать zenno, superForm и т.д.

Обратите внимание, что на конце нужно всегда ставить точку с запятой.

Теперь вот по этому имени мы и будем обращаться. С помощью свойств width и height зададим ширину и высоту нашей формы, а с помощью метода showDialog() заставим форму отобразиться.

Итак, со следующей строчки обращаемся по тому имени, что мы задали
form.
И видим всплывающие подсказки
128222


Мы видим тут свойства (значок гаечного ключа), методы (значок фиолетовой коробки) и некотороые другие вещи, на которых пока не будем акцентировать внимание. Выбираем свойство Width и жмем Enter (или щелкаем два раза мышкой). Теперь прописываем ту ширину, которая нам нужна.

form.Width = 400;
Аналогично поступаем и с высотой

form.Height = 550;
А теперь вызовем метод ShowDialog(), чтобы увидеть нашу форму.

form.ShowDialog();
Код целиком выглядит у нас так:
C#:
System.Windows.Forms.Form form = new System.Windows.Forms.Form();
form.Width = 400;
form.Height = 550;
form.ShowDialog();
Результат выполнения:
128223

Теперь добавим туда поле для ввода нашего кода и сохраним результат в переменную. Для этого используется уже отдельный класс TextBox. Код целиком
C#:
System.Windows.Forms.Form form = new System.Windows.Forms.Form();

form.Width = 400;
form.Height = 550;

System.Windows.Forms.TextBox smsBox = new System.Windows.Forms.TextBox();

smsBox.Left = 100; //отступ слева
smsBox.Top = 50; //отступ сверху
smsBox.Width = 150; //ширина поля

form.Controls.Add(smsBox); //добавляем созданную кнопку на поле
form.ShowDialog(); //отображаем форму

project.Variables["result"].Value = smsBox.Text; //записываем введенное значение в переменную result (создайте ее вручную, как всегда это делаете)
Результат
128224


Если вам хочется углубиться в создание форм, читайте эту статью - https://zenno.club/discussion/threads/okna-winforms-na-csharp-login-i-parol-sms-kod-knopka-prodolzhit-shablon.116652/

Продолжаем разбираться

В некоторых случаях нам не требуется создавать экземляр класса. Т.е. нам не понадобится ранее упомянутая конструкция вида

Название_Класса любое_имя = new Название_Класса();

Мы будем обращаться сразу же по названию класса

Название_Класса.метод();

или

Название_Класса.Свойство;

Давайте напишем в кубике c# следующее

instance.

instance – это у нас класс от разработчиков ZennoLab для работы с браузером. Тут нам не нужно писать что-то вроде

Instance name = new Instance();

Мы сразу же пишем instance и выбираем то свойство или метод, который нам нужен.
128225


Если мы используем свойство, нам не нужно будет ставить скобки и оно что-то нам вернёт, например:

project.Variables["result"].Value = instance.CachePath; //в переменную result ляжет путь к кэшу браузера
А вот в методах скобки уже нужны, и они что-то делают. Они тоже могут что-то возвращать после своей работы, но не всегда

instance.Reload(); //перезагрузка браузера
Вот так мы можем с вами очистить кэш (1 строка), куки (2 строка) и прокси (3 строка).

C#:
instance.ClearCache();
instance.ClearCookie();
instance.ClearProxy();
Есть ещё один случай, как мы можем получить объект какого-то класса. Нам его может вернуть свойство или метод другого класса. Формула такая

Название_класса любое_имя = Название_другого_класса.свойство;
Вот так мы получаем доступ к объекту класса Tab от разработчиков ZennoLab

Tab tab = instance.ActiveTab;
Теперь мы можем обращаться по тому имени, которое придумали (tab). Положим в переменную result исходный код страницы (DOM). Код целиком с самого начала

C#:
Tab tab = instance.ActiveTab;
project.Variables["result"].Value = tab.DomText;
128226

Т.е. свойство DomText класса Tab возвращает нам DOM страницы.

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

И ещё одно - все классы хранятся внутри пространства имён (namespace). Пока условимся, что пространство имён – просто некая коробка для хранения классов. Например, большинство классов от разработчиков ZennoPoster лежат внутри пространства имен под названием "ZennoLab.CommandCenter".

128227


Давайте подытожим, что мы узнали на текущий момент:
  • В c# есть готовые классы, предлагающие ту или иную функциональность.
  • Чтобы нам что-то сделать, надо получить доступ к объекту класса. Иногда нужно создавать экземпляр класса (через new), иногда мы обращаемся сразу по названию, иногда мы получаем его из свойств и методов другого класса.
  • После этого мы можем использовать свойства и методы этого класса.
  • Также мы уже умеем с вами создавать экземпляры класса Form, instance, Tab. Умеем вызывать некоторые их свойства и методы
#2

Давайте теперь немного разграничим такие понятия как класс, объект, тип данных и переменная.

Класс (class) – это некий чертёж, по которому создаются объекты. Например, если у нас класс человек, то объектом уже будет конкретный Вася Пупкин, Иван Иванов и т.д.

Объект, соответственно, это уже конкретный экземпляр класса.

Переменная – просто некая "коробка", которая хранит данные. Например, мы взяли из списка прокси и нам надо его куда-то положить. Как раз в переменную мы его и положим.

Тип данных же определяет, какого именно типа данные хранятся в переменной. Например, прокси мы будем хранить в строковом типе данных string, а вот возраст уже в числовом типе int.

Запоминать это особо не нужно, так, больше для справки. Это даже не официальные определения. Кому важна точность, лучше погуглить. Позже будете интуитивно всё это понимать. Я вообще постараюсь употреблять поменьше профессиональных терминов, ибо сам в них не особо разбираюсь (помните, я не программист). Главное понять суть, как нам работать.

В C# данные условно делятся на простые (строки, числа, …) и сложные (объекты, структуры, …).

В C# есть следующие простые типы данных:
  • Строка (string). Заключается в двойные кавычки
  • Число (byte, sbyte, short, ushort, int, uint, long, ulong, float, double, decimal)
  • Булевое значение (bool). Принимает только два значения: true (правда) или false (ложь)
  • char. Один символ
Отступление

Прежде чем перейти к рассмотрению простых типов данных, рассмотрим комментарии и вывод информации в лог.

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

Однострочный комментарий обозначает двумя косыми чертами

//я комментарий
Многострочные косой чертой и звездочкой

C#:
/*
Я многострочный комментарий.
Можно писать что угодно пока вы его не закроете
*/
Вывод в лог работает аналогично кубику "Логика" -> "Оповещение". Для этого нам надо воспользоваться классом project и его методом SendInfoToLog(). В качестве параметра он принимает строку (string). О параметрах метода мы поговорим позже.

project.SendInfoToLog("Выводим сообщение в лог ZennoPoster");
Не стоит принимать мои слова на веру, проверяйте всё на практике. Весь код, который вы видите в рамке, вы можете вставлять в кубик c# и смотреть, что происходит. Скопируйте строку выше и вставьте в C# кубик. Появилась ли информация в логе?

P.S. Если кто из старичков вдруг захочет поворчать, что project это вообще не class, а interface, то я в курсе, однако это не играет никакого значения. Сейчас (да и потом) забивать этим голову нет смысла, поэтому я буду называть project именно классом.

P.P.S. Если кто будет проходить уроки в ютубе или читать статьи про c#, то там вы метод SendInfoToLog() не встретите, т.к. он у нас от разработчиков ZennoPoster. В Visual Studio используется Console.WriteLine(), который похож на наш метод. Чтобы вам потестить примеры из статей в project maker, надо просто заменить Console.WriteLine на project.SendInfoToLog.


Переменные

Давайте чтобы в голове не возникала каша разберемся с этим подробнее. Начнем с кубиков. Мы с вами создаем переменные вот так
128230



И далее помещаем в эту "коробку" уже какую-то информацию (прокси, данные со страницы, пути к файлам и т.д.).

Когда мы работаем с этими переменными, мы вообще не задумываемся о типе данных. Т.е. мы можем класть туда что угодно: числа, строки, true/false, даты и т.д. В c# же нам надо следить за тем типом данных, который мы используем.

Я буду называть эти переменные "переменные уровня проекта". Их можно использовать в кубике c#. Вот так мы можем положить туда значение "Вася":

project.Variables["result"].Value = "Вася";

Т.е. обращение к "переменным уровня проекта" происходит с помощью конструкции

project.Variables["имя_переменной"].Value

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

project.Variables["result"].Value = 123; //error
Чтобы ошибки не было, мы должны цифры заключить в двойные кавычки:

project.Variables["result"].Value = "123";

"Переменная уровня проекта" (которая на самом деле вообще не переменная, но для удобства я называю ее именно так) у нас от разработчиков команды ZennoLab. В обычном c# её нет. Её стоит использовать только тогда, когда вам надо перебросить данные между кубиками. Т.е. если у вас идет кубик c#, в котором надо сохранить какие-то данные, вы можете сохранить их вот в такую переменную. В остальных же случаях мы используем простые переменные, о которых сейчас и поговорим.

Строка (string)

Создаём (инициализируем) переменную с типом string

string myVariable;
Как видим, сначала мы указываем тип переменной, а затем даём ей имя. Имя может быть почти что любым.

После того, как мы её создали, мы можем присвоить ей какое-то значение (т.е. положим в нашу коробку какие-то данные). При повторном обращении к переменной прописывать тип не нужно, иначе будет ошибка.
C#:
//создаём переменную с именем message
string message;
/*

если сделаем так, то получим ошибку, т.к. при повторном обращении указывать тип
уже не нужно(т.е. не надо прописывать string)
string message = "положили какие-то данные";
*/

message = "положили какие-то данные";
message = "а теперь поменяли данные";
P.S. Никогда не забывайте точку с запятой в конце, т.к. она означает окончание инструкции.

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

Знак равно (=) в c# называется знаком присваивания. Как вы уже поняли, он присваивает переменным какое-то значение. Математическое же равно пишется двойным знаком равенства, т.е. ==.

Также мы можем объединить эти две строчки в одну. Т.е. при создании переменной сразу же присвоить ей значение
C#:
//создание строковой переменной с именем mySuperNameVariable (имя может быть любым)
string mySuperNameVariable;

//присваиваем переменной mySuperNameVariable значение "Вася Пупкин"
mySuperNameVariable = "Вася Пупкин";

//а теперь создадим строковую переменную с именем car и сразу же присвоим ей значение
string car = "Audi";
Имена переменных желательно делать осмысленными, чтобы можно было понять, что там внутри. Я так и не научился их нормально именовать, но вам желательно к этому стремиться.

Нельзя создавать переменные с одинаковым именем. Т.е. если вы уже создали

string name = "Вася";
И позже в коде опять создадите переменную с тем же именем
string name = "Петя";
То получите в логе ошибку
128231


Имена переменных не могут совпадать с зарезервированными словами (например, нельзя давать переменной имя string, ведь так уже обозначается тип), не могут начинаться с цифры и ещё что-то там. Запоминать это не надо, редактор сам подскажет вам, если вы накосячили.
128232


Осмысленные части в имени переменной принято разделять регистром типа

string firstNameAndLastName = "Вася Пупкин";

И да, C# чувствителен к регистру, поэтому вот эти переменные – это три разных переменных:
C#:
//ошибки не будет, т.к. name и Name или naMe = это всё разные имена
string name = "Вася";
string Name = "Петя";
string naMe = "Коля";
Нельзя менять тип переменной. Т.е. если вы создали переменную

string name = "Вася";
То вы уже не сможете засунуть туда int (число)
name = 123; //приведёт к ошибке

Переменная будет существовать только в рамках одного кубика C#. Т.е. если вы в 1-м кубике C# создадите переменную

string name = "Вася";
А затем создадите новый кубик C# и попробуете к ней обратиться, то будет ошибка, что данной переменной не существует.

Давайте пока вы не запутались ещё раз повторим. По сути вот эти две строки одинаковые

C#:
string message = "сообщение";
project.Variables["message"].Value = "сообщение";
И в первом, и во втором случае мы с вами создали переменную с именем message с типом string и присвоили ей значение "сообщение".

Но к обычной переменной вы можете обращаться только в рамках того кубика, в котором она создана. Она как бы "уничтожается", когда мы переходим на новый кубик c#. К "переменной уровня проекта" же можно обращаться в любом кубике, и она сохранит то значение, которое вы ей присвоили.

Проверьте это на практике. Если мы выполним этот код

project.Variables["message"].Value = "сообщение";
То увидим, что значение изменилось
128233



Т.е. теперь мы с вами умеем заменять кубик "обработка переменных" c# кодом
128234

Мы можем объединить значения двух и более переменных в одну с помощью оператора +. Называется это конкатенацией.
C#:
string firstName = "Вася";
string lastName = "Пупкин";
string firstAndLastName = firstName + lastName;
project.SendInfoToLog(firstAndLastName); //получаем в логе " ВасяПупкин"
Аналогично делается и для "переменных уровня проекта" (только не забудьте их сначала создать вручную)

C#:
project.Variables["firstName"].Value = "Вася";
project.Variables["lastName"].Value = "Пупкин";
project.Variables["firstAndLastName"].Value = project.Variables["firstName"].Value + project.Variables["lastName"].Value;
project.SendInfoToLog(project.Variables["firstAndLastName"].Value); //получаем " ВасяПупкин"
Также никто не запрещает нам совмещать "переменные уровня проекта" и обычные строковые переменные, т.к. как я уже говорил, это по сути одно и тоже.

C#:
string firstName = "Вася";
string lastName = "Пупкин";
project.Variables["firstAndLastName"].Value = firstName + lastName;
project.SendInfoToLog(project.Variables["firstAndLastName"].Value); //получаем в логе " ВасяПупкин"

Также вы наверное обратили внимание, что имя и фамилия у нас "слиплись". А как же добавить пробел?

C#:
string firstName = "Вася";
string lastName = "Пупкин";
string firstAndLastName = firstName + " " + lastName + " Андреевич";
project.SendInfoToLog(firstAndLastName); //получаем "Вася Пупкин Андреевич"
Мы можем добавить всё, что хотим, главное использовать строки (т.е. или пишем в двойных кавычках или используем переменную string)

Числовые типы

Итак, смотрите.

double, float, decimal – это всё дроби. Пример написания:

C#:
double chislo = 2.23;
float chislo2 = 2.23f;
decimal chislo3 = 2.23m;
P.S. Помним, что "chislo", "chislo2" – просто имя переменной, которое может быть любым.

В основном вы будете пользоваться double. Decimal используется для операций, где важна точность, т.к. в C# (да и не только в нём) при сложении, вычитании, делении и т.д. может теряться точность в копейках.

int, uint, byte, sbyte, short, ushort, long, ulong – просто числа (без дроби). Пример написания:

C#:
int chislo4 = 25;
byte chislo5 = 3;
Зачем столько типов? Они отличаются размером занимаемой памяти. Так, byte занимает всего 1 байт, в то время как long уже 8 байт. При этом byte может принимать значения от 0 до 255, а long от -9 223 372 036 854 775 807 до 9 223 372 036 854 775 807. Если выйти за эти пределы, то будет ошибка.

Полную таблицу размеров можно загуглить. Нам в принципе всё равно, мы используем int, который принимает значения что-то вроде от -2 миллиардов до 2 миллиардов и занимает 4 байта.

Но что если всё же превысить допустимый лимит? Как вы уже наверное догадались, будет ошибка
128235



А сможем ли мы вывести наше число в лог? Давайте попробуем
128236


Как видим, мы получим ошибку. Почему? Метод SendInfoToLog() в качестве параметра принимает у нас только string. При попытке передать любой другой тип мы получаем ошибку. Здесь нужно выполнить преобразование из int в string, но об этом чуть позже. Пока же лишь упомяну, чтобы не было ошибки, мы будем вызывать метод Convert.ToString().

Операции с числами

Плюс, минус, умножить, поделить, остаток от деления, больше, меньше, равно – всё как в математике. Единственное, равно мы пишем вот так:

==

Главное следите, чтобы числа были одного типа.

C#:
int chislo = 2;
int chislo2 = 255;
int result = chislo + chislo2;
project.SendInfoToLog(Convert.ToString(result));
А давайте попробуем сделать тоже самое с типом string

C#:
string chislo = "2";
string chislo2 = "255";
string result = chislo + chislo2;
project.SendInfoToLog(result); //результат: 2255
Уловили разницу? Строки у нас соединяются, а вот числа уже складываются как в математике.

+= означает прибавить к текущему значению

C#:
int chislo = 100;
chislo += 50; //тоже самое, что chislo = chislo + 50;
project.SendInfoToLog(Convert.ToString(chislo)); //результат: 150

! (воскл знак) означает не.

++ означает увеличить значение на один. Называется инкремент

C#:
int chislo = 0;
chislo++;
project.SendInfoToLog(Convert.ToString(chislo)); //получим 1
Можно и уменьшать на 1, используя --
Есть ещё декремент, когда эти знаки ставятся не после переменной, а до. Т.е.

C#:
int chislo = 0;
++chislo;
В чём же тогда разница? Не забивайте пока себе этим голову. Мы это рассмотрим при прохождении циклов (если забуду, то напомните). Особо любопытные могут погуглить. В 99% случаев пользуемся инкрементом.

Boolean

Тип bool принимает два значения: true (правда) или false (ложь). Возвращается оно, когда мы проверяем какое-то условие. Например, существует ли элемент, является ли элемент таким-то и т.д.

Несколько примеров:

C#:
int chislo = 50;
int chislo2 = 100;
bool result = chislo == chislo2;
project.SendInfoToLog(Convert.ToString(result)); //т.к. значения переменных не равны, нам вернётся False

C#:
int chislo = 50;
int chislo2 = 100;
bool result = chislo < chislo2;
project.SendInfoToLog(Convert.ToString(result)); //т.к. 50 действительно меньше чем 100, нам вернется True

C#:
//вспоминаем, что воскл знак означает "не"
int chislo = 50;
int chislo2 = 100;
bool result = chislo != chislo2;
project.SendInfoToLog(Convert.ToString(result)); //т.к. 50 действительно не равно 100, нам вернется True

Char

char нужен для хранения одного символа. Пишется в одинарных кавычках. Пример:

C#:
char symb = '5';
project.SendInfoToLog(Convert.ToString(symb));

Преобразование типов

Мы можем преобразовывать один тип в другой. Для этого нужно пользоваться специальными методами. Например, с помощью метода Convert.ToString() можно преобразовать в строку (любой простой тип). Давайте попробуем

C#:
int chislo = 555;
string strChislo = Convert.ToString(chislo);
project.SendInfoToLog(strChislo); //в лог выводится, ошибок нет, значит всё получилось
А как на счёт Boolean или char? Всё аналогично

C#:
bool variable = true;
string str = Convert.ToString(variable);
project.SendInfoToLog(str);
C#:
char symb = 'a';
string stroka = Convert.ToString(symb);
project.SendInfoToLog(stroka);
P.S. А давайте вспомним, что тут вообще происходит. Смотрите, Convert - это– класс. Только он не от разработчиков ZennoPoster, он у нас из самого c#. Раз это класс, у него есть свойства и методы. ToString() – один из таких методов. В скобках мы указываем параметр. Параметры метода мы ещё не проходили, но уже касались в этом уроке при изучении метода SendInfoToLog().

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



C#:
string strChislo = "123";
int chislo = Convert.ToInt32(strChislo); //для преобразования в int

string strBool = "true";
bool bl = Convert.ToBoolean(strBool); //для преобразования в bool

string strDouble = "2.01";
double d = Convert.ToDouble(strDouble); //для преобразования в double

Ну это основные. Разные short, byte и т.д. пропустим, но эти там тоже все есть.

#3

Продолжим разбираться с классами. Мы уже с вами познакомились с несколькими классами, такими как instance, tab, htmlelement и др.

Все эти классы были сделаны разработчиками ZennoPoster. Именно поэтому спрашивать о них бесполезно на форумах или у chatGpt (если только предварительно не скормить ему справку). Информацию о них нужно искать на форуме zennolab.com или в самой документации (https://help.zennolab.com/en/v7/zennoposter/7.1.4/webframe.html#topic1.html). Для этого в яндексе/гуле пишем что-то вроде «instance.SetwindowSize что делает site:zennolab.com» и читаем.

Однако помимо того, что нам предоставляют разработчики ZennoPoster, внутри самого C# (т.е. в .Net) есть масса собственных классов. Плюс есть масса библиотек, созданных сторонними разработчиками. Плюс вы можете создавать и свои собственные классы. Всё это мы будем изучать позже.

Параметры методов

Опять же для простоты условимся, что аргументы = параметры. Дело в том, что в некоторые методы мы должны передавать аргументы, чтобы они работали, а в некоторые нет. Что это такое? Легче всего объяснить на практике.

instance.ClearProxy();

Этот метод очищает прокси браузера. Видите пустые скобки? Значит данный метод не имеет никаких параметров (аргументов). А теперь посмотрим на метод с параметрами.

instance.SetProxy("127.0.0.1:8888");
https://help.zennolab.com/en/v7/zennoposter/7.1.4/webframe.html#topic377.html

Этот метод устанавливает прокси в браузер. Видите, в скобках мы прописали наш прокси? Это и есть параметр.

Значит запоминаем. Некоторые методы не требуют параметров, а некоторые требуют. Как узнать надо ли их передавать? Можно посмотреть документацию к методу (выше давал ссылки), а можно посмотреть и в самом Project Maker при написании кода. Подсказка появится после того, как вы поставите скобку.
128238

===
128239


Обратите внимание, что параметры перечислены через запятую. Сначала указывается тип параметра, а затем его имя (которое может быть любым и не играет роли, это имя просто подсказывает нам, что мы должны прописать).

Если мы возьмём метод SetProxy() (смотри скрин выше), то мы видим, что первым параметром передаем string (вспоминаем прошлый урок), затем bool, bool, bool. Пример:
instance.SetProxy("127.0.0.1:8888", false, true, true, true);
Также увидеть есть ли параметры у метода можно при выборе метода. Когда мы с вами пишем instance и ставим точку, то у нас появляется набор свойств и методов класса instance. Там же дается информация о том, что делает метод (на англ) и имеет ли он параметры.
128240

Обязательные и необязательные параметры

Методы могут иметь как обязательные, так и необязательные параметры. Что это значит? Если параметр обязательный, а мы его не передали (не прописали), то будет ошибка. Если же параметр необязательный, то мы сможем запустить метод и без него. Например, вышеупомянутый метод

instance.SetProxy("…");

Имеет один обязательный (прокси) и четыре необязательных параметра (использовать ли проксифаер, устанавливать ли таймзону, гео, webrtc). Т.е. первый мы обязаны прописать в любом случае, а остальные по желанию. Понять сколько у метода обязательных параметров можно посмотрев на подсказку. Все необязательные параметры заключены в квадратные скобки.
128241

Рассмотрим ещё один пример

instance.ClearCookie();
128242

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

Необязательные параметры имеют значение по умолчанию. Т.е. если вы ничего не указали, то подставляется то значение, которое решили разработчики. Что это за значение смотрим в документации.

Перегрузки метода

Один и тот же метод может иметь несколько перегрузок. Это означает, что у нас есть несколько возможностей передать параметры.

Вернёмся к нашему методу по установке прокси. Когда мы его прописываем, то в подсказке видим, что он имеет три перегрузки:
128243


Пощёлкайте по стрелкам, и вы увидите, как будут меняться тип и количество передаваемых параметров. В первой перегрузке мы должны обязательно прописать наш прокси в string и четыре по желанию в bool. Теперь нажмем на стрелку и посмотрим вторую перегрузку:

128244


Здесь у нас уже два параметра, при этом первый имеет тип ProxySettings, а второй NetworkSettings. Они оба обязательные.

Нажмём на стрелку ещё раз и увидим третью перегрузку:
128245



Ага, тут у нас уже три обязательных параметра (сразу смотрим на типы, это string-int-string) и 6 необязательных.

Давайте вспомним метод из предыдущего пункта Convert.ToString()
128246


Как видим, у него вообще 36 перегрузок. Именно поэтому у нас в качестве параметра может быть как int, так и bool, char, uint и многие другие типы.
Тип передаваемых параметров

Прежде чем прописывать какой-то из параметров, нам надо посмотреть на его тип. Обычно это простые типы, пройденные нами в предыдущем уроке (string, int, bool и т.д.), но необязательно.
128247


Посмотрим на вторую перегрузку этого метода ещё раз
128248


Мы видим, что тут в качестве 1-го параметра идёт "сложный" тип данных ProxySettings, а в качестве 2-го NetworkSettings.

Соответственно, прежде чем мы сможем их прописать, нам надо будет разобраться, что это за типы и как с ними работать (читаем справку, форум зенно, гугл, chatGpt). Далее их создать, заполнить и затем уже передать.

Возвращаемый тип данных

Методы могут не просто что-то делать, но и что-то возвращать. А возвращать они опять же будут какой-то тип данных (class, если вам так проще). Это мы можем увидеть в подсказке, либо в документации. Давайте вернемся к уже ставшему нами любимому методу по установке прокси. Глядите сюда
128249


void означает, что метод ничего не возвращает. Значит, он просто что-то делает и на этом всё.

А теперь посмотрим на другой метод

instance.GetCookie();
128250


Видим, что данный метод возвращает string (значит результат его работы надо положить в переменную с типом string).

C#:
string myCookieBrowser = instance.GetCookie();
return myCookieBrowser;
Возвращать значение необязательно. Т.е. вот такая строка тоже будет работать

instance.GetCookie();
Просто конкретно в данном случае мы не увидим результат работы метода, т.к. его единственным предназначением является возврат кук.

Как и в случае с параметрами возвращаемый тип данных необязательно будет простым. Например вот тут
128251


Нам вернётся массив IExtension. Т.е. разбираемся, что это, создаем, потом уже присваиваем ей результат работы метода.

#4

Условия if

В любой программе условия используются постоянно. "Если переменная равна тому-то, то делай это", "Если элемент не найден, делай вот это" и т.д. Условие прописывается следующим образом
C#:
int age = 18;
if(age == 18)
{
            project.SendInfoToLog("age действительно 18");
}
Вспоминаем, что равно в C# пишется двойным знаком равно ==. В вышеприведенном коде написано следующее:
  • Первой строкой мы объявили переменную типа int с именем age и присвоили ей значение 18
  • Второй строкой мы говорим "если age равна 18, то выполни то, что находится внутри {}. В данном случае выведи сообщение в лог".
Строк внутри фигурных скобок {} может быть сколько угодно.

Мы также можем прописать, что делать если условие НЕ выполнено.

C#:
int age = 11;

if(age == 18)
{
            project.SendInfoToLog("age действительно 18");
}
else
{
            project.SendInfoToLog("age не равна 18. она равна " + Convert.ToString(age));
}
В данном случае мы написали, что если age равна 18, то выведи в лог " age действительно 18", в ином же случае выведи " age не равна 18. она равна " и значение переменной age. Т.к. age у нас int, а метод SendInfoToLog() принимает string, мы преобразовали её в string с помощью метода Convert.ToString(). Мы также использовали оператор + , чтобы соединить эти две строки.

Ладно, что-то я отвлекаюсь. Всё равно на текст наверное оно не очень воспринимается, на практике ещё закрепим. А пока продолжим.

Мы также можем прописать несколько различных условий, используя else if. Т.е. у нас будет если age == 18, делай то-то, если она равна 20, то делай то-то, если 30, то делай то-то, а если ничему из этого, то делай вот это.

C#:
int age = 20;

if(age == 18)
{
            project.SendInfoToLog("age действительно 18");
}
else if(age == 20)
{
            project.SendInfoToLog("age равна 20");
}
else if(age == 20)
{
            project.SendInfoToLog("age равна 20");
}
else if(age == 30)
{
            project.SendInfoToLog("age равна 30");
}
else
{
            project.SendInfoToLog("age равна " + Convert.ToString(age));
}
Финальный else {] можно было не прописывать, если он вам не нужен.

Помимо знака равно, мы можем использовать и другие, такие как не равно != больше или меньше > <, >= <=.

C#:
int age = 20;

//обратите внимание, что выполнится только первое условие, остальные опустятся
if(age != 18)
{
            project.SendInfoToLog("age действительно НЕ РАВНА 18");
}
else if(age == 20)
{
            project.SendInfoToLog("age равна 20");
}
else if(age == 20)
{
            project.SendInfoToLog("age равна 20");
}
else if(age == 30)
{
            project.SendInfoToLog("age равна 30");
}
else
{
            project.SendInfoToLog("age равна " + Convert.ToString(age));
}

C#:
int age = 10;

if(age >= 18)
{
            project.SendInfoToLog("age действительно НЕ РАВНА 18");
}
else if(age == 20)
{
            project.SendInfoToLog("age равна 20");
}
else if(age < 18)
{
            project.SendInfoToLog("age равна 20");
}
Внутри условий может использоваться любой тип данных, в том числе string, bool и т.д.

C#:
string message ="hello world";
bool flag = true;

if(message == "hello world")
{
            project.SendInfoToLog("message у нас hello world");
}

if(flag == true)
{
            project.SendInfoToLog("flag true");
}

Если у вас то, что в {} занимает только одну строку, их можно не прописывать

C#:
bool flag = true;
if(flag == true) project.SendInfoToLog("можно так");

if(flag == true)
            project.SendInfoToLog("так тоже можно с переносом на след строку");

Когда у нас в скобках идёт bool, можно прописывать сокращенно

C#:
bool flag = true;

//тоже самое что if(flag == true)
if(flag) project.SendInfoToLog("оки");

//тоже самое что if(flag == false)
if(!flag) project.SendInfoToLog("no");
Помимо if-else существует также switch, но его мы рассматривать пока не будем, дабы не забивать голову лишним. Вернёмся к нему позже, когда вы немного "окрепните". А так по идее можно жить и без switch.

Использование "и" и "или" в условиях

Мы можем проверять сразу несколько утверждений. С помощью && задается "и", а с помощью || "или".

C#:
string str = "message";
bool flag = true;

if(str == "message" && flag == true)
{
            project.SendInfoToLog("оба условия выполнены");
}
C#:
string str = "message";
bool flag = true;

if(str == "message" || flag == true)
{
            project.SendInfoToLog("оба условия выполнены");
}
Существует также знак ^. Вернёт true, только если одно из условий является истинным, а другое ложным. На практике я таким не пользовался.

C#:
string str = "message";
bool flag = true;

if(str == "message" ^ flag == false)
{
            project.SendInfoToLog("оба условия выполнены");
}
Видимость переменных

Переменная, объявленная внутри фигурных скобок {}, будет видна только там. Давайте разберемся на практике
128252


Мы получаем ошибку, код даже не запустится. Посмотрите на описание "The name 'test' does not exist in the current context". [Строка: 9; Cтолбец: 23]" (имя test не существует в данном контексте).

К слову, нумерацию строк мы можем вкл щёлкнув правой кнопкой мыши по полю, где пишем код и выбрав "нумерация строк"
128253


Также можно вкл в настройках по умолчанию
128254


Итак, вернёмся к нашей ошибке. Т.к. переменная test была создана внутри {}, то и использовать её можно только там. За пределами её уже нет. Чтобы она была видна за фигурными скобками, надо было её создать до них

C#:
string message ="hello world";
string test = "тестируем видимость переменнной";

if(message == "hello world")
{
            project.SendInfoToLog(test);
}
project.SendInfoToLog(test);
128255


Переменная будет видна только в рамках одного кубика C#. Смотрите, я создал два кубика
128256


В "кубике 1" я прописал следующую строчку

string message ="hello world";
Если я теперь попробую в "кубике 2" к ней обратиться, то получу ошибку "переменная message не существует в данном контексте"
128257


В "кубике 2" уже можно заново создавать переменные с теми же именами. Т.е. если у вас в "кубике 1" есть переменная с именем message, то в "кубике 2" тоже можно её создавать, никакой ошибки не будет
128258


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


128260


В [""] указывается имя переменной. Не забудьте только создать её вручную, как вы это раньше всегда делали.

Чтобы постоянно не прописывать эту длинную строку, вы можете нажать правой кнопкой мыши по редактору кода и выбрать там из списка.
Видео по теме - youtube.com/watch?v=EpTRp6deeYI

Списки

У нас есть списки из .Net и списки, созданные разработчиками ZennoPoster. Работают они на 99% одинаково.

Давайте для простоты начнём с зенковского списка. Доступны те же операции, что и в кубике "Операции над списком" – добавление в список, удаление и т.д.

Все строки внутри этого списка имеют тип string.

Первым делом создаём его вручную
128261



Теперь получим к нему доступ

IZennoList list = project.Lists["Список 1"];
  • Список 1 – это то имя, которое вы задавали, когда создавали список.
  • list – это имя переменной с типом IZennoList. Оно может быть любым
Теперь обращаясь по имени переменной(list), мы можем совершать различные операции с нашим списком (путем вызова методов). Рассмотрим наиболее популярные
  • Add() – добавляет строку в список
  • AddRange() – добавляет данные из другого списка
  • Bind() – привязка файла к списку, если вы это не делали вручную при его создании
  • Clear() – очищает список
  • Contains() – проверяет наличие строки в списке (полное совпадение). Возвращает bool
  • Count – это свойство. Возвращает количество элементов в списке
  • Insert() – позволяет вставить строку в определенное место в списке (куда-то в середину, например)
  • Remove() – удаляет строку по значению
  • RemoveAt() – удаляет строку по номеру (счёт с нуля)
Доступ к конкретному элементу получаем путём указания порядкового номера в []. Обратите внимание, что счёт начинается с нуля (т.е. первый элемент в списке идет под индексом ноль)

C#:
//инициализируем список
IZennoList list = project.Lists["Список 1"];

//очищаем список
list.Clear();

//добавляем элементы в список
list.Add("Вася");
list.Add("Петя");
list.Add("Коля");

//выводим в лог первый элемент списка
string firstStr = list[0];
project.SendInfoToLog(firstStr);

//проверяем наличие в списке
bool checkPety = list.Contains("Петя");
if(checkPety == true)
{
            project.SendInfoToLog("петя есть в списке");
}

//удаляем первый элемент из списка и потом выводим первый элемент в лог
list.RemoveAt(0);
firstStr = list[0];
project.SendInfoToLog(firstStr);

//получаем количество строк в списке
int countList = list.Count;
project.SendInfoToLog("В списке столько строк - " + Convert.ToString(countList));
128262


Теперь про списки, которые находятся внутри .Net. Это своего рода "виртуальный" список, который будет жить в рамках одного кубика. Аналогию можно провести с переменными. Т.е. если мы берём переменную

project.Variables["number"].Value = "";
То она живёт на протяжении всего проекта в любых кубиках c#. Если же мы берём такую переменную

string message = "";
То она будет существовать только в рамках кубика, где была создана.

Аналогично с нашими списками. Если мы возьмём список IZennoList, то он тоже живёт на протяжении всего проекта. Если же мы берем "виртуальный" список, то он живёт в рамках одного кубика c#. Создаётся он так

List<string> myName = new List<string>();
P.S. myName – это имя, может быть любым.

И далее для него актуальны всё те же методы, что мы прошли для IZennoList.

Зачем он тогда нужен вообще, если данные на пк не сохраняются? Ну например, вы что-то спарсили со страницы в этот список, затем обработали каким-то образом, а потом уже сохраняете на пк с помощью метода File.ReadAllLines(). Этот метод пройдём позже. Плюс тут мы можем использовать не только тип string, но и любые другие, как простые (int, bool, char и т.д.), так и сложные (HtmlElement, mHatch и др.).

Также здесь есть несколько дополнительных методов:
  • Shuffle() – перемешивает элементы списка
  • Sort() – сортирует список
C#:
List<string> myName = new List<string>();

//добавляем элементы в список
myName.Add("Вася");
myName.Add("Петя");
myName.Add("Коля");
myName.Add("Катя");
myName.Add("Надя");
myName.Add("Лена");

//выводим в лог первый элемент списка
string firstStr = myName[0];
project.SendInfoToLog(firstStr);

//проверяем наличие в списке
bool checkPety = myName.Contains("Петя");
if(checkPety == true)
{
            project.SendInfoToLog("петя есть в списке");
}

//удаляем первый элемент из списка и потом выводим первый элемент в лог
myName.RemoveAt(0);
project.SendInfoToLog(myName[0]);

//получаем количество строк в списке
int countList = myName.Count;
project.SendInfoToLog("В списке столько строк - " + Convert.ToString(countList));

//перемешиваем список и выводим в лог 1-й элемент
myName.Shuffle();
project.SendInfoToLog(myName[0]);

//сортируем список по алфавиту и выводим в лог 1-й элемент
myName.Sort();
project.SendInfoToLog(myName[0]);
Видео для закрепления -

Циклы

Циклы нужны для совершения повторяющихся действий. Например, это может быть перебор списка, перебор элементов на странице и т.д. В C# есть циклы for, while, do-while, foreach. Мы рассмотрим только for, чтобы не забивать голову лишним. Без остальных можно жить, но мы всё равно вернёмся к ним позже.

Мы помним, что в списке элементы получаются по индексу

list[0] – первый элемент
list[1] – второй элемент

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

C#:
List<string> myName = new List<string>();

//добавляем элементы в список
myName.Add("Вася");
myName.Add("Петя");
myName.Add("Коля");
myName.Add("Катя");
myName.Add("Надя");
myName.Add("Лена");

//получим кол-во элементов
int count = myName.Count;
int i=0;

//перебираем список
for(i=0;i<count;i = i+1)
{
            string el = myName[i];
            project.SendInfoToLog(el);
}

project.SendInfoToLog("================");

//перемешаем список
myName.Shuffle();

//переберем ещё раз, используя более короткую запись
for(int j=0;j<myName.Count;j++)
{
            project.SendInfoToLog(myName[j]);
}
Иногда нам будет нужно пропустить какую-то строчку в списке. Для этого используется continue. В примере ниже мы пропустим все строчки, которые содержат слово "Вася".

C#:
List<string> myName = new List<string>();

//добавляем элементы в список
myName.Add("Вася");
myName.Add("Петя");
myName.Add("Коля");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Катя");
myName.Add("Надя");
myName.Add("Лена");
myName.Add("Вася Пупкин");
myName.Add("Вася Иванов");
myName.Add("Иванов Вася");

int sch = 0;
for(int i=0;i<myName.Count;i++)
{
            bool checkName = myName[i].Contains("Вася");
            if(checkName == true)
            {
                        continue;
            }
            sch = sch + 1;
            project.SendInfoToLog(myName[i]);
}

project.SendInfoToLog("Количество строк, в которых нет слова Вася - " + Convert.ToString(sch));
Иногда нам надо покинуть цикл, если мы нашли в нём то, что искали. Для этого используется break. Давайте выйдем из цикла сразу же, как обнаружим Надю.

C#:
List<string> myName = new List<string>();

//добавляем элементы в список
myName.Add("Вася");
myName.Add("Петя");
myName.Add("Коля");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Катя");
myName.Add("Надя");
myName.Add("Лена");
myName.Add("Вася Пупкин");
myName.Add("Вася Иванов");
myName.Add("Иванов Вася");

string name = "";
bool flag = false;
for(int i=0;i<myName.Count;i++)
{
            bool checkName = myName[i] == "Надя";
            if(checkName == true)
            {
                        flag = true;
                        name = myName[i];
                        break;
            }
}

if(flag == true)
{
            project.SendInfoToLog("нашли Надю и положили её в переменную name - " + name);
}
else
{
            project.SendInfoToLog("Нади не было в списке");
}

Циклы можно вкладывать "один в другой" (вложенные циклы). Только имя счётчика меняйте.

P.S. Счётчиком называют переменную, с которой начинается цикл. В нашем случае это было int i=0.

Нормальный пример сходу придумать не получается, поэтому просто бредовый приведу вам. На практике ещё встретим такую ситуацию, я думаю.

C#:
List<string> myName = new List<string>();

//добавляем элементы в список
myName.Add("Вася");
myName.Add("Петя");
myName.Add("Коля");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Катя");
myName.Add("Надя");
myName.Add("Лена");
myName.Add("Вася Пупкин");
myName.Add("Вася Иванов");
myName.Add("Иванов Вася");

string name = "";
bool flag = false;
for(int i=0;i<myName.Count;i++)
{
            bool checkName = myName[i] == "Надя";
            if(checkName == true)
            {
                        flag=true;
                        name = myName[i];

                        for(int j=0;j<10;j++)
                        {
                                   project.SendInfoToLog("надю нашли. наш j на текущий момент - " + Convert.ToString(j));
                        }
            }
}

if(flag == true)
{
            project.SendInfoToLog("нашли Надю и положили её в переменную name - " + name);
}
else
{
            project.SendInfoToLog("Нади не было в списке");
}

Видео по теме -

Генерация своей ошибки

Мы можем сгенерировать собственную ошибку. Зачем это надо? Например, если какой-то элемент не найден, то возможно нам нет смысла продолжать выполнять наш проект. Для этого генерируем ошибку и идем по красной ветке. При этом в логе будет нормальное описание и номер строки, где была сгенерирована ошибка.

C#:
project.SendInfoToLog("вывод в лог №1");
throw new Exception("моя собственная ошибка");
project.SendInfoToLog("вывод в лог №2");
128263


Поиск ошибок

В будущем в вашем проекте у вас может быть множество кубиков C#. Имейте ввиду, что ошибка в одном кубике приводит к тому, что все кубики C# перестают работать. Если возникает ошибка первым делом надо определить, в каком именно кубике C# она возникла. Делается это также, как с обычными кубиками.

Давайте создадим несколько кубиков и умышленно забудем поставить точку с запятой в одном из них
128264



Идём в лог -> жмём правой кнопкой мыши по ошибке -> скопировать айди действия
128265



Идём в "редактирование" -> "поиск по проекту" (или жмём "ctrl F")
128266


Вставляем айди и жмём найти. Мы попадём на кубик с ошибкой
128267



В логе мы видим на какой именно строке ошибка и её описание. Иногда он может косячить и незначительно ошибаться с номером строки
128268



Иногда номер строки не отображается вовсе. Как в таком случае понять, где ошибка? Обычно, когда это случается, он вам позволит запустить кубик. А ошибка возникнет по ходу выполнения. В этом случае можно выполнить код пошагово. Для этого жмём сюда и у вас появляется красный кружок
128269


Теперь запускаем кубик и выполняем код построчно
128270



Так вы сможете дойти до ошибки.

Ещё один вариант – натыкать выводов в лог и посмотреть, какой именно не выведется.

try-catch

Если мы знаем, что в каком-то месте кода может произойти ошибка, и мы хотим её обработать, мы можем использовать специальную конструкцию try-catch.

C#:
try
{
            project.SendInfoToLog("псевдокод");
            //тут 100500 строк кода
            throw new Exception("вдруг где-то возникла ошибка");
}
catch(Exception e)
{
            project.SendInfoToLog("Описание ошибки" - e.Message);
}
finally
{
            project.SendInfoToLog("я выполнюсь в любом случае");
}
В блок try {} мы помещаем код, в котором может возникнуть ошибка (выход по красной ветке).

Если ошибка действительно возникает, выполнение перескакивает в блок catch {} и выполняется код, написанный там.

Если ошибка НЕ возникает, то выполнится только то, что внутри try {}. То, что в catch {}, выполняться не будет.

finally {} выполнится в любом случае, независимо от того, попали мы в catch или нет. finally можно не прописывать вовсе, если он вам не нужен.

Быстрое создание переменных одного типа

Если вам нужно объявить несколько переменных одного типа их можно перечислить через запятую:

C#:
string firstname = "Вася", lastname = "Иванов", job = "Сантехник";
project.SendInfoToLog(firstname);
Интерполяция строк

Ранее мы с вами соединяли строки с помощью оператора +, например:

C#:
string firstname = "Вася", lastname = "Иванов", job = "Сантехник";
string fullInformation = firstname + " " + lastname + " " + job;
project.SendInfoToLog(fullInformation);
Но вы также можете использовать более удобный синтаксис, называемый интерполяцией строк:

C#:
string firstname = "Вася", lastname = "Иванов", job = "Сантехник";
string fullInformation = $"наш рабочий это: {firstname} {lastname}. Его специализация {job}";
project.SendInfoToLog(fullInformation);
Т.е. ставим знак $ перед кавычками, а все переменные для вывода заключаем в {}.

Константы

Мы можем запретить изменять значение переменной, прописав слово const.
const string name = "вася";
Такая переменная называется константой. При попытке поменять значение получаем ошибку
128271


#5
lock

Когда мы пишем код на C#, мы должны с вами сами контролировать, чтобы разные потоки не могли одновременно обратиться к одному и тому же файлу, иначе можно получить ошибку (вы же не хотите, чтобы одна и та же почта из файла попала сразу к двум-трём потокам?). Это может быть список, таблица, база данных. Для этого существует конструкция lock.

Суть её очень простая. Если какой-то из потоков зашёл внутрь lock {}, то пока он оттуда не выйдет, другие должны будут ждать.

Как это можно написать

C#:
IZennoList list = project.Lists["Список 1"];

lock(SyncObjects.ListSyncer)
{
            //обращение к списку, таблице, бд
  
            string str = list[0];
            list.RemoveAt(0);
}
Обсудим то, что у нас пишется в скобках (SyncObjects.ListSyncer). Это у нас объект, не позволяющий другим потокам залезать внутрь {}. Есть следующие готовые заготовки:
  • SyncObjects.ListSyncer – для списков
  • SyncObjects.TableSyncer – для таблиц
  • SyncObjects.InputSyncer – для буфера обмена
  • SyncObject – общее
Разделение весьма условное. Т.е. никто вам не запрещает использовать SyncObjects.ListSyncer и для таблиц, это просто поделили для удобства.

Мы также можем создавать и свои объекты для лока. Для этого создаем общий код
128277



И на 33-й строке мы уже видим одну из наших заготовок
128278


По образу и подобию создаём свои, меняем только имя (SyncObject)
128279


И теперь можем их использовать в кубике c#, обращаясь таким образом: имя_класса.имя_объекта

C#:
lock(CommonCode.OneLock)
{
            //обращение к списку, таблице, бд
  
            string str = list[0];
            //list.RemoveAt(0);
}
Зачем же создавать собственные объекты для лока и вообще зачем их столько?

Дело в том, что один такой лок

C#:
lock(CommonCode.OneLock)
{
  
}
будет действовать на все участки кода, где вы его используете. Если не ошибаюсь, действие даже может распространяться даже на разные шаблоны (как-нибудь мы это проверим путём эксперимента).

Соответственно, если вы будете использовать какой-то один объект для всех файлов (например тот, что мы написали выше), то при работе в множество потоков у вас может возникнуть нехилая такая "пробка" – очередь из потоков.

Поэтому будет неплохо для каждого файла создавать свой объект для лока. Например, у вас есть файл с прокси. Создайте для него

public static object ProxyLock = new object();
И используйте его. Для файла с ключевыми словами создайте новый

public static object KeyLock = new object();
И используйте его

C#:
lock(CommonCode.KeyLock)
{
            string str = list[0];
}
Для какой-то таблицы свой и т.д. Это не строгое правило. Если в вашем проекте используется не очень много файлов или вы не планируете работать в большое количество потоков, то так заморачиваться не стоит, можно использовать 1-2 лока и успокоиться на этом. Но в ином случае лучше заморочиться.

Работа с таблицами

По тому же принципу, что мы работали со списками, используя IZennoList, мы можем работать с таблицами. Т.е. первым делом вручную создаем таблицу, даём ей имя и привязываем к файлу. Потом работаем с кодом.

Инициализация

IZennoTable table = project.Tables["Таблица 1"];

Добавление строк

C#:
table.ColSeparator = ";"; //задаём разделитель столбцов
string str1 = "вася;пупкин;сантехник"; //добавляемая строка
string str2 = "петя;пчелкин;электрик";
string str3 = "гена;букин;продавец обуви";
table.AddRow(str1);
table.AddRow(str2);
table.AddRow(str3);
Читаем ячейку

C#:
string str = table.GetCell(0, 0); //первая колонка первый столбец (счёт с нуля)
project.SendInfoToLog(str);
Устанавливаем значение в ячейку

table.SetCell(0, 0, "Жора"); //первая строка первый столбец установим "Жора"

Удаляем колонку

table.DeleteColumn(2); //удаляем третью колонку (счёт с нуля)

Удаляем строку

table.DeleteRow(0); //удаляем первую строку

Получаем количество колонок и строк

C#:
int colCount = table.ColCount; //кол-во колонок
int rowCount = table.RowCount; //кол-во строк
Получаем строку

C#:
List<string> list = table.GetRow(0).ToList(); //получаем первую строку
for(int i=0;i<list.Count;i++)
{
            project.SendInfoToLog(list[i]);
}
Очищаем таблицу

table.Clear();
Массивы

Массивы – тоже самое что списки, только имеют они строго определенную длину. Т.е. если мы создали массив из 5 элементов, то изменить это количество мы уже не сможем.

Инициализация массива:

C#:
int[] mass = new int[4];
mass[0] = 12;
mass[1] = 55;
mass[2] = 33;
mass[3] = 23;
Если вам заранее известны значения, которые вы хотите записать, то можно так

int[] mass = new int[] {124,12412,21,57,89};
Используемый тип может быть любым. Хоть string, хоть HtmlElement (и т.д.).

Получение длины массива происходит с помощью свойства Length (не Count как у списков)

int longMass = mass.Length;

Перебор в цикле аналогичен перебору списков

C#:
int[] mass = new int[] {124,12412,21,57,89};
for(int i=0;i<mass.Length;i++)
{
            project.SendInfoToLog(Convert.ToString(mass[i]));
}
Обращение к элементам как у списка, т.е.

int m = mass[0]; //получим первый элемент

Словарь (Dictionary)

Словарь – это коллекция, позволяющая нам хранить данные в формате "ключ значение".

Инициализируем словарь и добавляем значения

C#:
Dictionary<string, string> sl = new Dictionary<string, string>();
sl.Add("Имя", "Вася"); //сначала ключ, потом значение
sl.Add("Фамилия", "Иванов");
Ключ всегда уникальный! Т.е. мы не сможем добавить ещё один ключ "Имя" в этот же словарь, будет ошибка.

Обращение к элементу происходит по ключу, а получаем мы значение этого ключа

C#:
Dictionary<string, string> sl = new Dictionary<string, string>();
sl.Add("Имя", "Вася");
sl.Add("Фамилия", "Иванов");
string str = sl["Фамилия"];
project.SendInfoToLog(str); //Результат: Иванов
Очистка словаря

sl.Clear();

Проверка наличия ключа

C#:
Dictionary<string, string> sl = new Dictionary<string, string>();

sl.Add("Имя", "Вася");
sl.Add("Фамилия", "Иванов")

bool checkName = sl.ContainsKey("Имя");
return checkName; //True
Проверка наличия значения

C#:
Dictionary<string, string> sl = new Dictionary<string, string>();
sl.Add("Имя", "Вася");
sl.Add("Фамилия", "Иванов");

bool checkName = sl.ContainsValue("Василий");
return checkName; //False
Количество элементов в словаре

int count = sl.Count;

Пример перебора словаря

C#:
Dictionary<string, string> sl = new Dictionary<string, string>();
sl.Add("Имя", "Вася");
sl.Add("Фамилия", "Иванов");

for(int i=0;i<sl.Count;i++)
{
            string key = sl.ElementAt(i).Key;
            string value = sl.ElementAt(i).Value;
  
            project.SendInfoToLog($"Ключ: {key}. Значение: {value}");
}
Также добавлю, что на практике применяется довольно часто.

Var

Когда мы используем var, мы говорим компилятору, чтобы он сам подставил тип переменной. Он смотрит на "правую" часть (какой тип там находится) и исходя из этого определяет тип переменной. Примеры

C#:
var str = "строка"; //это у нас будет string
var number = 123; //это будет int
var tab = instance.ActiveTab; //а это Tab
var el = tab.FindElementByXPath("//p", 0); //это HtmlElement
Object

object является базовым для всех типов данных.

Так как все классы в .NET являются производными Object, каждый метод, определенный в Object классе, доступен во всех объектах в системе. Производные классы могут переопределять некоторые из этих методов, включая:

  • Equals: поддерживает сравнения между объектами.
  • Finalize: выполняет операции очистки перед автоматическим восстановлением объекта.
  • GetHashCode: создает число, соответствующее значению объекта для поддержки использования хэш-таблицы.
  • ToString: производит текстовую строку, читаемую человеком, которая описывает экземпляр класса.
Тут надо понимать, что object это не var и не dynamic. Хоть вы и можете положить туда любой тип данных свойства и методы этих типов вам будут недоступны

C#:
object str = "строка";
//str = str.Replace("строка", "123"); - будет ошибка, мы не можем пользоваться методами для string если у нас переменная object
object number = 123;
return str;
Dynamic

dynamic позволяет нам создавать переменную с любым типом. Ключевым тут является то, что это ключевое слово опускает проверку типов во время компиляции. Лучше не использовать и вот почему:

  • У вас не будет подсказок во время написания кода
  • Узнать об ошибке вы сможете только после запуска кода
Пример

C#:
dynamic str = "строка";
str = str.Replace("строка", "123");
dynamic number = 123;
Методы string

Мы рассмотрим наиболее популярные свойства и методы. Это во многом аналогично вот этому кубику
128280


Все нижеизложенные примеры мы ещё раз посмотрим в видео и также вы найдёте их в шаблоне "4".

Замена

Для этого используем метод Replace(). В качестве 1-го параметра передается старое значение, а в качестве 2-го новое

C#:
//создадим переменную с именем text и присвоим ей значение
string text = "Вася пошёл на прогулку и вернулся только в 20:00";

//выведем в лог значение
project.SendInfoToLog(text);

//заменим фразу "на прогулку" на "гулять" и выведем в лог
string newText = text.Replace("на прогулку", "гулять");
project.SendInfoToLog(newText);
//сохраним новое значение в переменной уровня проекта "name"
project.Variables["name"].Value = newText;
Перевод в верхний и нижний регистр

Делается методами toUpperCase() и toLowerCase().

C#:
//создадим новую переменную с именем "toUpperVariable" и переведем в верхний регистр и выведем в лог
string toUpperVariable = text.ToUpper();
project.SendInfoToLog(toUpperVariable);


//а теперь в нижний регистр
string toLowerVariable = text.ToLower();
project.SendInfoToLog(toLowerVariable);
Проверка существования

С помощью метода Contains() мы можем проверить, есть ли в нашей переменной нужное слово. Обратите внимание, что метод возвращает тип bool, а значит и результат мы должны положить в переменную с типом bool. Но вот незадача. Чтобы вывести результат в лог (с помощью project.SendInfoToLog()) мы должны передать string, а не bool. Что же делать? Нужно превратить bool в string и только потом выводить в лог. Не переживайте, эту тему мы ещё затронем позже

C#:
bool checkText = text.Contains("Вася");
string checkTextToString = Convert.ToString(checkText);
project.SendInfoToLog(checkTextToString);
Количество символов

Метод Count() позволяет нам посчитать количество символов в тексте. Также можно это сделать и с помощью свойства Length. Обратите внимание, что этот метод возвращает уже int, а значит и результат мы ложим в переменную с типом int. Ну, а далее чтобы вывести в лог или сохранить результат в переменную уровня проекта, нам надо превратить int в string аналогично тому, как делали выше

C#:
//узнаём количество символов в тексте с помощью Count и выводим в лог
int countSymbolsInText = text.Count();
string strCountSymbolInText = Convert.ToString(countSymbolsInText);
project.SendInfoToLog(strCountSymbolInText);

//узнаём количество символов в тексте и выводим в лог с помощью свойства Length
int countSymbolsInTextLengthProperties = text.Length;
string strCountSymbolInTextLengthProperties = Convert.ToString(countSymbolsInTextLengthProperties);
project.SendInfoToLog(strCountSymbolInTextLengthProperties);
Чем начинается или заканчивается строка

Метод StartsWith() позволяет проверить, начинается ли строка с нужной фразы. Метод EndsWith() проверяет, заканчивается ли строка указанный нами фразой. Оба метода в результате выполнения возвращают bool

C#:
//узнаем начинается ли переменная с Вася пошёл
bool startText = text.StartsWith("Вася пошёл");
string strStartText = Convert.ToString(startText);
project.SendInfoToLog("Результат выполнения начинается ли текст с фразы 'Вася пошёл' - " + strStartText);

//узнаем заканчивается ли переменная на 20:00
bool endText = text.EndsWith("Вася пошёл");
string strEndText = Convert.ToString(endText);
project.SendInfoToLog("Результат выполнения чем заканчивается текст - " + strEndText);
Где находится искомая фраза

Метод IndexOf() позволяет узнать, с какого символа начинается указанная нами фраза. Если её вообще нет, то вернётся -1.

C#:
//откуда начинается искомая фраза (прогулк)
int checkWalk = text.IndexOf("прогулк");
string strCheckWalk = Convert.ToString(checkWalk);
project.SendInfoToLog($"искомое слово начинается с {strCheckWalk} символа");

Вставка фразы

Метод Insert() позволяет вставить новую строку в переменную. 1-м параметром передаем с какого символа вставляем (int), а вторым саму фразу (string).

C#:
//вставляем фразу начиная с 5-го символа
string newTextAddPhrase = text.Insert(5, "Пупкин ");
project.SendInfoToLog(newTextAddPhrase);
Удаление части строки

Метод Remove() позволяет удалить часть строки. 1-м параметром передаем откуда удаляем (int), 2-м параметром сколько удаляем (int)

C#:
//удаляем часть строки
string deleteText = text.Remove(0, 4);
project.SendInfoToLog(deleteText);
Удаление пробелов в начале и конце

Метод Trim() удалит пробелы в начале и конце строки. Метод TrimStart() удалит только в начале, а TrimEnd() только в конце.

C#:
//удаляем пробелы
string strForEdit = "     строка с пробелами    ";
project.SendInfoToLog("так выглядит строка с пробелами - " + strForEdit);
string newStrForEdit = strForEdit.Trim();
project.SendInfoToLog("убрали пробелы - " + newStrForEdit);

Ещё пара слов

Переменные можно объединять друг с другом с помощью оператора +. Это называют конкатенацией строк. Также можно пользоваться и другим способом, продемонстрированном ниже

C#:
//объявляем переменные
string str1 = "первая строка";
string str2 = "вторая строка";

//объединяем
string str3 = str1 + str2;
project.SendInfoToLog("результат - " + str3);

//добавим пробел
string str4 = str1 + " " + str2;
project.SendInfoToLog("добавили пробел, чтобы нормально смотрелось");

//объединяем 2-м способом
string str5 = $"ещё можно вот так соединять: {str1} {str2}";
project.SendInfoToLog(str5);
Я бы ещё вот что хотел до вас донести. Вот смотрите метод project.SendInfoToLog() у нас в качестве параметра принимает строку. Мы можем как писать её напрямую

project.SendInfoToLog("пишем текст");
Можем указать переменную (но только в string)

C#:
string str = "пишем текст";
project.SendInfoToLog(str);
Можем производить конкатенацию прямо там

C#:
string str = "пишем текст";
project.SendInfoToLog(str + " добавляем что угодно".Insert(0, " И ещё добавим "));
#6

Порядок передачи параметров в методы

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

C#:
Tab tab = instance.ActiveTab;
tab.Navigate(referrer: "google.ru", url: "ya.ru");

Цепочка вызовов

Мы можем существенно сокращать объём написания кода, используя так называемую "цепочку вызовов". Давайте я лучше объясню на примерах. Вот так мы совершаем переход на сайт

C#:
Tab tab = instance.ActiveTab;
tab.Navigate("ya.ru");
Тут мы сначала положили Tab в переменную и далее к ней обращаемся.

Но мы можем обращаться и сразу без создания переменной таким образом

instance.ActiveTab.Navigate("ya.ru");
Поскольку instance.ActiveTab возвращает нам объект Tab, компилятор сам понимает, что теперь он работает с Tab и предоставляет нам свойства и методы для работы с ним.

Эта цепочка может быть сколь угодно длинной

string str = instance.ActiveTab.MainDocument.FindElementByXPath("/html/body/div/div[@class]", 0).FullTagName;
  • instance.ActiveTab возвращает Tab. Мы получили доступ к его свойствам и методам
  • MainDocument является свойством класса Tab. Он возвращает тип Document, так что теперь мы получили доступ к нему
  • У класса Document есть метод FindElementByXpath() (да, этот метод доступен не только в классе Tab, но и в классе Document) который вернул нам тип HtmlElement
  • У HtmlElement есть свойство FullTagName, который возвращает тип string. Именно поэтому мы положили результат в тип string.
На всякий случай. Вышеизложенную строку можно целиком расписать вот так (делайте как вам удобнее, но первая время я советую расписывать целиком):

C#:
Tab tab = instance.ActiveTab;
Document doc = tab.MainDocument;
HtmlElement el = doc.FindElementByXPath("/html/body/div/div[@class]", 0);
string str = el.FullTagName;
Цикл While

Аналогичен циклу for, но записывается чуть иначе. Переберем список

C#:
List<string> list = new List<string>()
{
            "Вася",
            "Петя",
            "Коля"
};

int i=0; //счётчик
while(i < list.Count)
{
            project.SendInfoToLog(list[i]);
            i++; //увеличение счётчика
}

Иногда while используется для создания бесконечных циклов. Только не забывайте предусмотреть выход из него, чтобы программа не зависла

C#:
List<string> list = new List<string>()
{
            "Вася",
            "Петя",
            "Коля"
};

int i=0; //счётчик
string str = "";
while(true)
{
            try {
                        str = list[i];
            }
            catch{
                        project.SendInfoToLog("перебрали список");
                        break;
            }
            project.SendInfoToLog(list[i]);
            i++; //увеличение счётчика
}
Цикл do while

В отличии от while, тот код, что находится в do {} будет выполнен в любом случае, даже если условие в скобках ложно.

C#:
List<string> list = new List<string>()
{
            "Вася",
            "Петя",
            "Коля"
};

int i=0; //счётчик
string str = "";
do
{
            project.SendInfoToLog(list[i]);
            i++; //увеличение счётчика
}
while(i < list.Count);
Цикл foreach

Удобен для перебора коллекций, но не всегда его можно использовать

C#:
List<string> list = new List<string>()
{
            "Вася",
            "Петя",
            "Коля"
};

foreach(string s in list)
{
            project.SendInfoToLog(s); //в s автоматически кладется сначала первый элемент списка, затем 2-й и т.д. пока не переберем всё
}
Условие switch

Всё, что делается на switch, можно сделать на if-else. А вот в обратную сторону это не работает. Но в некоторых случаях код читатется легче именно со switch.

C#:
List<string> list = new List<string>()
{
            "Вася",
            "Петя",
            "Коля"
};


switch(list[0]) //тут переменная, значение которой проверяем
{
            case "Вася": //тут возможное значение переменной
                        project.SendInfoToLog("Первый элемент в списке Вася"); //тут действия, если значение совпало. можно сколько угодно строк
                        break; //так мы говорим, что действия закончены
            case "Петя":
                        project.SendInfoToLog("Первый элемент в списке Петя");
                        break;
            case "Коля":
                        project.SendInfoToLog("Первый элемент в списке Коля");
                        break;
            case "Михаил":
            case "Василий":
                        project.SendInfoToLog("Михаил или Василий");
                        break;
            default: //если не совпало никакое из значений
                        project.SendInfoToLog("первый элемент в списке неизвестен");
                        break;
}
Практика по пройденным темам:

#10
Введение в общий код


Для чего вам общий код

Давайте я сначала объясню вам чем вам может быть полезен общий код. Разберем пример клика по элементу на c# (код составлен для стр https://ya.ru/).

C#:
HtmlElement el = null;
Tab tab = instance.ActiveTab;
Random r = new Random();

for (int i = 0; i<30; i++)
{
    //пробуем отыскать элемент
    el = tab.FindElementByXPath("//div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]", 0);

    //если найден, пауза и выходим из цикла
    if (!el.IsVoid)
    {
        Thread.Sleep(r.Next(1000, 3000));
        break;
    }

    //выход по ошибке, если элемент не появился
    if (i == 29) throw new Exception("не дождались появления элемента //div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]");

    //пауза 1 сек
    project.SendInfoToLog("ждём появления элемента //div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]");
    Thread.Sleep(1000);
}

//клик
tab.FullEmulationMouseMoveToHtmlElement(el);
Thread.Sleep(r.Next(300, 1000));
tab.FullEmulationMouseClick("left", "click");

//ожидание загрузки и пауза
if (tab.IsBusy) tab.WaitDownloading();
Thread.Sleep(r.Next(1000, 3000));
Вам не кажется, что слишком дохрена кода для одного действия? Но что если я скажу вам, что тот же самый код можно будет вызвать всего одной строчкой вроде:

browser.Click(project, instance, "//div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]");
Вот именно этому мы с вами и научимся. Помимо создания своей библиотеки вы также можете выносить в общий код и логику проекта. Правда, делать это грамотно я вас научить не смогу, но понимание того, как это делается, вы получите.

Библиотеки
Откроем ссылки из GAC. Здесь мы с вами видим те пространства имен (библиотеки), что уже используются в проекте. Т.е. в данном случае у нас добавлено System, System.Core и т.д. Можно зайти на microsoft, вбить это название в поиск и ознакомиться, что там есть.

linkGac.gif
Кнопкой "удалить" можно убрать ненужные. Кнопкой "добавить" можно добавить новые. Давайте нажмём её. Тут мы с вами видим все доступные нам библиотеки (1), их версию (2) и путь к ним (3)

linksbor.png
Чтобы в ваш проект добавить какую-то из этих библиотек, надо нажать по ней два раза кнопкой мыши (или выделить и нажать "ок"). Для примера добавим System.Net. Видим, что она появилась в общем списке

systemNetAdd.gif
Но это далеко не все доступные нам библиотеки. Если мы нажмём на кнопку обзор и зайдём в папку с ZennoPoster, то увидим множество dll, которые также можно добавить. Для примера добавим Newtonsoft.json, которая нужна для работы с JSON

newtonsoftadd.gif
Помимо того, что тут есть, мы можем добавлять и сторонние библиотеки. Закидывать и надо в папку ExternalAssemblies, ну а далее точно также жмём "обзор" и добавляем. Что при этом важно учесть:
  • У нас в ZennoPoster используется .Net Framework 4.6.2, в то время как весь мир сидит уже на .Net. Поэтому прежде чем добавлять библиотеку, убедитесь, что добавляемая библиотека совместима с .Net Framework 4.6.2 (т.е. ищите старые версии библиотек)
  • Сами dll можно искать как где-то на форумах, так и скачивать с Nuget, используя Visual Studio (позже будет видео).

Знакомимся с общим кодом
Добавляем общий код

obshcode.png
В самом верху у нас прописаны директивы using. Это подключенные пространства имён. Можно обойтись и без них, но тогда вам придётся писать полный путь при обращении к классам (имя_пространства_имен.название_класса.название_метода()). Например, чтобы нам вызвать File.Move(...), нам надо будет написать System.IO.File.Move().

Возможно вы также заметили вверху две вкладки: "общий код" (это где мы сейчас находимся) и "директивы using".

dirUsing.png
Так вот, вкладка "директивы using" нужна именно для кубиков c#. Т.е. если вы добавили какую-то библиотеку и не хотите прописывать полный путь начиная с пространства имен, вам надо будет прописать директиву using именно в этой вкладке.

Вернёмся к общему коду. После директив using мы с вами видим namespace ZennoLab.OwnCode {}. Вспоминаем, что namespace (пространство имён) по сути просто коробка для классов. Далее у нас идёт сам класс public class CommonCode{}, внутри которого мы уже можем создавать свои свойства и методы

Давайте мы с вами переместим наш код клика, написанный в кубике c# в общий код. Для этого внутри класса создаём метод

method.png
Наш метод состоит из модификатора доступа (public), возвращаемого значения (void), имени (Click) и параметров (IZennoPosterProjectModel project, Instance instance)
  • Модификаторы доступа нужны для того, чтобы понять, где этот метод можно будет использовать. Написав public мы сказали, что этот метод будет доступен везде (в том числе и кубике c#). Бывает также internal (почти тоже самое, что public), protected (в наследуемых классах) и private (только в этом классе, т.е. в кубике c# он уже доступен не будет)
  • Методы могут не возвращать значений (пишем void), а могут и вернуть абсолютно любой тип (тогда указываем его. например string, int, bool, HtmlElement и т.д.). Мы это уже проходили.
  • Имя может быть почти любым. Действуют те же правила, что и для имён переменных. Публичные (public) методы принято называть с большой буквы, а приватные (private) с маленькой
  • Про параметры вы тоже уже знаете. Объясню, зачем мы с вами прописали IZennoPosterProjectModel project, Instance instance. Дело в том, что по умолчанию в общем коде объекты project и instance недоступны. Если мы хотим их использовать, нам надо их "перекинуть" из кубика c# в общий код. Позже ещё коснёмся этой темы. А пока же просто имейте ввиду, что вам надо прописать эти параметры, если хотите в своём методе воспользоваться project или instance
Теперь переносите код внутрь фигурных скобок
128282


Нам осталось научиться вызывать наш метод из кубика c#. Для этого нам надо создать экземпляр данного класса, а затем обратиться к нашему методу Click(), не забыв передать project и instance в качестве параметров

C#:
CommonCode myname = new CommonCode();
myname.Click(project, instance);
Как видим, в этот раз нам понадобилось всего 2 строчки вместо 40.

Возвращаемое значение методов
Вспоминаем наши первые уроки. Методы могут как возвращать какое-то значение, так и нет. Если метод ничего не возвращает, мы пишем void. Если же да, то пишем тип, который он возвращает. А в самом коде в самом конце мы должны прописать return возвращаемая_переменная. В предыдущем примере мы с вами написали метод клика, вот только логика ожидания элемента зашита прямо в него. Это не очень хорошо, т.к. ожидание элемента нужно не только при совершении клика, но и при вводе текста, при парсинге и т.д. Давайте вынесем этот код в отдельный метод WaitElement(). Создаем новый метод с этим именем

C#:
public HtmlElement WaitElement(IZennoPosterProjectModel project, Instance instance, string xpath)
{
    HtmlElement el = null;
    Tab tab = instance.ActiveTab;
    Random r = new Random();

    for (int i = 0; i<30; i++)
    {
        //пробуем отыскать элемент
        el = tab.FindElementByXPath("//div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]", 0);

        //если найден, пауза и выходим из цикла
        if (!el.IsVoid)
        {
            Thread.Sleep(r.Next(1000, 3000));
            break;
        }

        //выход по ошибке, если элемент не появился
        if (i == 29) throw new Exception("не дождались появления элемента //div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]");

        //пауза 1 сек
        project.SendInfoToLog("ждём появления элемента //div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]");
        Thread.Sleep(1000);
    }
    return el;
}
Обратите внимание, что возвращаемый тип у нас HtmlElement, т.к. именно его нам возвращает метод FindElementByXpath(...). Также мы добавили параметр методу с типом string, чтобы передавать xpath путь элемента

Теперь мы можем удалить лишний код из метода Click и прописать туда метод WaitElement. Я также добавил параметр xpath, чтобы мы могли передавать xpath путь до элемента

click.png
C#:
public void Click(IZennoPosterProjectModel project, Instance instance, string xpath)
{
    HtmlElement el = null;
    Tab tab = instance.ActiveTab;
    Random r = new Random();

    el = WaitElement(project, instance, xpath);

    //клик
    tab.FullEmulationMouseMoveToHtmlElement(el);
    Thread.Sleep(r.Next(300, 1000));
    tab.FullEmulationMouseClick("left", "click");

    //ожидание загрузки и пауза
    if (tab.IsBusy) tab.WaitDownloading();
    Thread.Sleep(r.Next(1000, 3000));
}
Параметры методов
Вы можете передавать сколько угодно параметров какого угодно типа. Мы также можем передавать необязательные параметры, которые будут иметь значение по умолчанию. Единственное, идти они должны только после обязательных параметров. Давайте "прокачаем" наш метод Click, добавив возможность выбирать, будем ли мы эмулировать мышку и будем ли мы брать паузы до и после кликов

C#:
public void Click(IZennoPosterProjectModel project, Instance instance, string xpath, bool emulationMouse, bool sleep=true)
{
    HtmlElement el = null;
    Tab tab = instance.ActiveTab;
    Random r = new Random();

    el = WaitElement(project, instance, xpath);

    //клик
    if(emulationMouse)
    {
        tab.FullEmulationMouseMoveToHtmlElement(el);
        Thread.Sleep(r.Next(300, 1000));
        tab.FullEmulationMouseClick("left", "click");
    }
    else
    {
        el.Click();
    }


    //ожидание загрузки и пауза
    if(sleep)
    {
        if (tab.IsBusy) tab.WaitDownloading();
        Thread.Sleep(r.Next(1000, 3000));
    }

}
Приватные методы
Выше я упомянал про то, что методы с модификатором public доступны везде, а private только в своём классе. Смотрите, сейчас наш метод WaitElement() доступен как в текущем классе, так и в любом другом (в том числе кубике c#). Давайте убедимся в этом

128283

Но по сути зачем нам публичный метод WaitElement? Едва ли мы будем использовать его в кубике c#. Поэтому давайте поменяем public на private и убедимся, что он больше недоступен в кубике c#

private.png privateWait.png
К слову, если вы вообще не пропишите модификатор доступа для метода, то он будет считаться private. А если вы не пропишите модификатор доступа для класса, то он будет считаться internal

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

Бывают ситуации, что нам нужно вернуть несколько значений из метода. В таком случае можно использовать вот такую штуку

Общий код

C#:
public void test(out string result1, out string result2)
{
    //любая логика

    //по ходу кода
    result1 = "Возвращаемое значение 1";

    //любая логика

    //по ходу кода
    result2 = "Возвращаемое значение 2";
}
кубик c#

C#:
CommonCode myname = new CommonCode();
string res1=string.Empty, res2=string.Empty;

myname.test(out res1, out res2);
project.SendInfoToLog($"{res1}\n{res2}");
Т.е. нам надо
  1. В методе параметры передать с использованием out
  2. вернуть эти значения по ходу выполнения кода, просто присвоив им нужное значение
  3. В кубике c# (или другом методе) создать переменные
  4. когда будем вызывать метод, мы прописываем их с использованием out
Конструктор

У классов есть такая штука, как конструктор. В нём происходит инициализация объекта, т.е. мы можем с вами прямо при создании экземляра класса присвоить значения каким-то переменным, выполнить проверки и т.д. Конструктор – это как метод, который выполняется сразу при создании экземпляра класса.

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

C#:
public class CommonCode
    {
        /// <summary>
        /// Lock this object to mark part of code for single thread execution
        /// </summary>
        public static object SyncObject = new object();
    
        IZennoPosterProjectModel project;
        Instance instance;
        Tab tab;
    
        public CommonCode(IZennoPosterProjectModel _project, Instance _instance)
        {
            project = _project;
            instance = _instance;
            tab = instance.ActiveTab;
        }
    
        /// <summary>
        /// Метод перехода на страницу
        /// </summary>
        /// <param name="url">куда переходим</param>
        public void Navigate(string url)
        {
            tab.Navigate(url);
            tab.WaitDownloading();
        }

        // Insert your code here
    }
В кубике c# же инициализация класса теперь будет выглядеть следующим образом:

C#:
CommonCode com = new CommonCode(project, instance);
com.Navigate("zennolab.com");
Поля и свойства

Полями в общем коде называют обычные переменные. Их принято делать только приватными. Это означает, что пользоваться ими мы сможем только в том классе, в котором их создали. Т.е. например, в кубике c# вы их не увидите. В примере выше полями у нас являются

C#:
IZennoPosterProjectModel project;
Instance instance;
Tab tab;
Но ничто не мешало добавить нам и другие переменные вроде:

C#:
string name;
int age;
Если мы попробуем посмотреть на них из кубика c#, то ничего не увидим:
128284

Чтобы они стали доступными, нам нужно добавить модификатор public.

C#:
public string name;
public int age;
128285


128286


Но так делать не рекомендуется.

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

Свойства "работают в связке" с приватными полями.

Давайте вместо поля age создадим свойство Age (публичные члены класса принято называть с большой буквы, а приватные с маленькой) и сделаем так, чтобы нельзя было поставить его меньше нуля.

C#:
int age;
    
public int Age
{
    get
    {
        if(age < 0) age = 0; //это наша проверка
        return age; //а тут мы возвращаем результат
    }
    set
    {
        age = value; //означает присвоить устанавливаемое значение
    }
}
Теперь давайте поработаем со свойством Age в кубике c#

128287

Как видите, когда мы присвоили 25, мы и получили 25. Но если мы присваиваем -5, то получаем 0 (а не -5).

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

public string Job {get;set;}

И теперь можно пользоваться им в кубике c# как обычной переменной.
128288

Заключение

По общему коду я пробежался довольно поверхностно и возможно кого-то запутал. Вам надо просто начать его использовать, и вы начнете потихоньку "въезжать". Без его освоения полноценно писать на C# нет смысла, но юзать какие-то сниппеты можно.

Чтобы не удлинять статью ещё больше, я вам в отдельном файле прикрепил шпаргалку по ZennoPoster API и некоторым распространенным классам .Net. Там есть примеры кода, которые можно вставлять в кубик c# и смотреть, что происходит. Пользуйтесь.

Надеюсь, эта статья была кому-то полезна и теперь c# стал для вас чуточку проще, чем до её прочтения. Всех благ.

Видео - https://youtu.be/CPzjZJxzA44
 

Вложения

Последнее редактирование:

kul0n

Client
Регистрация
10.03.2016
Сообщения
86
Благодарностей
14
Баллы
8
Походу сама длинная конкурсная статья, нужна еще одна номинация) Спасибо за проделаный труд!
 

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 802
Благодарностей
1 473
Баллы
113
Если вы работаете в VS, то вы весь проект будете писать на C#
Бред полный, не правда!
Из плюсов VS – там лучше работает отладчик кода
ппц, та таже зенка написана именно там... все дальше не читаю, это полный 13,14здец ТС ты извини ну подача вообще жестяк...
 

ShikoFess

Client
Регистрация
21.12.2017
Сообщения
149
Благодарностей
117
Баллы
43
По началу казалось, что это будет очередной пересказ любой книжки по коду, по типу переменные и их типы бла бла бла. Даже немного начали закатываться глаза, но глянув дальше начальной главы понял что, я ошибся. Много знаний в 1 статье, труд виден невооруженным глазом. Пробежался пока поверхностно, позже вчитаюсь более основательно в аспекты которые плохо понимал. Огромное спасибо за подробный материал!
 

ShikoFess

Client
Регистрация
21.12.2017
Сообщения
149
Благодарностей
117
Баллы
43
Бред полный, не правда!

ппц, та таже зенка написана именно там... все дальше не читаю, это полный 13,14здец ТС ты извини ну подача вообще жестяк...
Фу таким быть, предисловие ты видимо не читал. Да и если ты с ним в корне не согласен по этим пунктам, это не говорит о том что дальше такая же ерунда будет. Если начало это обложка книги, то тут хороша будет поговорка "Не суди книгу по обложке".
 

web3grep

Client
Регистрация
27.11.2023
Сообщения
22
Благодарностей
60
Баллы
13
спасибо за перевод с программисткого на обычный) А то меня буквально рубить начинает когда я за тех.литературу сажусь XD
 
  • Спасибо
Реакции: kul0n

Gunjubasik

Client
Регистрация
30.05.2019
Сообщения
3 563
Благодарностей
1 367
Баллы
113
Итак, сначала небольшое предисловие (оно же предостережение).

Я не программист. Это значит, что некоторые вещи я могу объяснять неправильно, могу упускать какие-то важные моменты, могу не оптимально писать код, могу даже неверно использовать терминологию. Это не профессиональный курс.

Я уже несколько лет использую c# в ZennoPoster. Моя основная задача – это максимально простое объяснение материала. Мне нужно, чтобы вы поняли C# и начали использовать в своих шаблонах, а дальше уже при желании можете проходить уроки программистов (на том же ютубе или metanit), если вам это нужно.

И ещё момент – я буду работать в обычном кубике C#, а не Visual Studio. Дело в том, что я не программист и потому никогда особо не пользовался VS. Раньше его не было, и я привык работать так.

Есть несколько причин, по которым я не пытаюсь перейти на VS:
  • Если вы работаете в VS, то вы весь проект будете писать на C#. Тут нужно учитывать, что далеко не все кубики имеют аналог на C#. Кроме того, некоторые вещи банально удобнее делать на кубиках, плюс вы первое время в любом случае будете совмещать у себя и то и другое.
  • В VS есть свои баги, которые надо уметь отлавливать (я имею ввиду в связке с ZennoPoster). Возможно сейчас это уже всё пофиксили, но раньше они точно были. Даже те, кто юзает VS, чаще всего пишут проект там, а потом переносят всё равно переносят в общий код
  • Лично мне неудобно тестировать проект. Мне не нравится, как они вставили браузерное окно в VS.
Из плюсов VS – там лучше работает отладчик кода, плюс сам код набирается быстрее, чем в кубике C#. Плюс там гораздо удобнее выстраивать структуру проекта (особенно если он будет большим). Если вы уверены, что сможете написать весь код на C#, плюс сам шаблон будет большим, то делайте выбор в пользу VS.

То, что я показываю вам в кубике C#, будет работать и VS (главное правильно его подключить - https://zennolab.atlassian.net/wiki/spaces/RU/pages/1375109121/Visual+Studio). Если вам удобнее тренироваться там, я не против.

Классы и объекты

Частенько объясняют так. Класс – это некий чертёж, а объектом будут уже конкретные вещи, которые по этому чертежу создаются.

Возьмём для примера человека. Человек – это класс (class).

У него есть рост, вес, цвет волос, количество пальцев и т.д. Т.е. какое-то описание. Всё это – свойства (properties).

Также человек может ходить, бегать, прыгать, читать, писать и т.д. Т.е. какие-то действия. Это всё – методы (methods).
Посмотреть вложение 128218


А вот Вася Пупкин 27 лет или Елена Ивановна 20 лет – это уже объекты, экземпляры класса человек.

Возьмём теперь автомобили. Автомобиль = class. Они у нас имеют цвет, мощность, тип и т.д. Это свойства. Автомобиль может ехать. Это метод. А ВАЗ 2107 будет объектом, экземпляром класса автомобиль.

Поняли?

А теперь перенесёмся в контекст языка C#. Нам с вами доступна большая библиотека классов "из коробки" (Net Framework). Например, класс для работы с датой и временем (DateTime), класс для работы с файлами (FileInfo), класс для работы с папками (DirectoryInfo) и ещё 100500 таких классов. Некоторые из этих классов уже подключены по умолчанию и можно сразу начинать их вызывать. Другие же нужно сначала подключить, но об этом в другой раз.

Кроме того, что доступно "из коробки", у нас есть классы от разработчиков команды ZennoLab. Посмотреть их можно тут - https://help.zennolab.com/en/v7/zennoposter/7.1.4/webframe.html#topic1.html

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

Посмотреть вложение 128219

Например, «HtmlElement» нужен для работы с отдельными элементами, которые мы находим на странице. Вы можете узнать ширину и высоту элемента (1), получить его местоположение относительно верхнего края браузера (2), получить имя тэга (3), получить текст (4), проверить существование (5) и т.д. Это всё свойства.
Посмотреть вложение 128220

Теперь посмотрим методы. Мы можем кликнуть по элементу(1), перетащить элемент (2), получить его потомков (3) и т.д. Имейте ввиду, что методы (в отличии от свойств) имеют круглые скобки на конце.

Посмотреть вложение 128221

Аналогично с другими объектами (классами). Так, «instance» - это браузер. Мы можем получить все вкладки, узнать или установить таймзону, получить или установить куки, установить разрешение и т.д. «tab» - это вкладка браузера. Здесь мы можем находить элементы, двигать мышку, эмулировать клавиатуру.

Помимо "базовой коробки" и "коробки от ZennoLab" мы можем ставить и сторонние библиотеки от какого-нибудь программиста Пети.

Почему я акцентировал на этом внимание. Смотрите, сейчас популярны нейросети, помогающие писать код. Так вот, он хорошо разбирается в "базовой коробке", но очень мало знает о том, что там написали разработчики ZennoLab или какой-то Петя (если только библиотека Пети не стала популярной). Поэтому имейте это ввиду, когда обращаетесь к AI за помощью.

Есть и ещё один момент, о котором стоит упомянуть. Наша "базовая коробка" (уж извините за такой слэнг) всё время пополняется новыми классами, т.е. новым функционалом. Но конкретно в ZennoPoster этого самого обновления не происходит. Мы застряли на .Net Framework (конкретную версию не помню), которая отстаёт от мира на пару лет. Это не страшно, но имейте ввиду, что если ваш код не запускается на ZennoPoster, то или не подключены нужные библиотеки или они в принципе не поддерживаются в ZennoPoster.

На самом деле всё не так страшно, потом это в голове уложится. Всё, что будет даваться в статье, будет работать сразу, так что можете открывать кубик c#, вставлять туда код и смотреть, как это работает.

Основы написания кода

Итак, давайте вникать в тему. Мы с вами знаем, что есть некие классы, у которых есть свойства и методы, позволяющие делать всякие штуки. Т.к. класс это чертеж, то нам нужно сначала создать конкретный экземляр данного класса (объект) и уже него мы просить что-либо сделать.

Давайте потренируемся на Windows Forms. Вы можете использовать формы вместо входных настроек. Также они могут быть полезны, если, например, нужно вручную указать какой-то код для сайта (например, при авторизации в телеграмм или whatsapp).

Есть такой класс как Form, позволяющий работать с формами. Его полное имя System.Windows.Forms.Form. Первым делом создадим по этому чертежу конкретного "Васю Пупкина". Формула тут такая:

Название_Класса любое_имя = new Название_Класса();

System.Windows.Forms.Form form = new System.Windows.Forms.Form();

form – это имя экземпляра класса (объекта). Оно может быть любым (почти). Т.е. вместо form вы можете написать zenno, superForm и т.д.

Обратите внимание, что на конце нужно всегда ставить точку с запятой.

Теперь вот по этому имени мы и будем обращаться. С помощью свойств width и height зададим ширину и высоту нашей формы, а с помощью метода showDialog() заставим форму отобразиться.

Итак, со следующей строчки обращаемся по тому имени, что мы задали
form.
И видим всплывающие подсказки
Посмотреть вложение 128222

Мы видим тут свойства (значок гаечного ключа), методы (значок фиолетовой коробки) и некотороые другие вещи, на которых пока не будем акцентировать внимание. Выбираем свойство Width и жмем Enter (или щелкаем два раза мышкой). Теперь прописываем ту ширину, которая нам нужна.

form.Width = 400;
Аналогично поступаем и с высотой

form.Height = 550;
А теперь вызовем метод ShowDialog(), чтобы увидеть нашу форму.

form.ShowDialog();
Код целиком выглядит у нас так:
C#:
System.Windows.Forms.Form form = new System.Windows.Forms.Form();
form.Width = 400;
form.Height = 550;
form.ShowDialog();
Результат выполнения:
Посмотреть вложение 128223
Теперь добавим туда поле для ввода нашего кода и сохраним результат в переменную. Для этого используется уже отдельный класс TextBox. Код целиком
C#:
System.Windows.Forms.Form form = new System.Windows.Forms.Form();

form.Width = 400;
form.Height = 550;

System.Windows.Forms.TextBox smsBox = new System.Windows.Forms.TextBox();

smsBox.Left = 100; //отступ слева
smsBox.Top = 50; //отступ сверху
smsBox.Width = 150; //ширина поля

form.Controls.Add(smsBox); //добавляем созданную кнопку на поле
form.ShowDialog(); //отображаем форму

project.Variables["result"].Value = smsBox.Text; //записываем введенное значение в переменную result (создайте ее вручную, как всегда это делаете)
Результат
Посмотреть вложение 128224

Если вам хочется углубиться в создание форм, читайте эту статью - https://zenno.club/discussion/threads/okna-winforms-na-csharp-login-i-parol-sms-kod-knopka-prodolzhit-shablon.116652/

Продолжаем разбираться

В некоторых случаях нам не требуется создавать экземляр класса. Т.е. нам не понадобится ранее упомянутая конструкция вида

Название_Класса любое_имя = new Название_Класса();

Мы будем обращаться сразу же по названию класса

Название_Класса.метод();

или

Название_Класса.Свойство;

Давайте напишем в кубике c# следующее

instance.

instance – это у нас класс от разработчиков ZennoLab для работы с браузером. Тут нам не нужно писать что-то вроде

Instance name = new Instance();

Мы сразу же пишем instance и выбираем то свойство или метод, который нам нужен.
Посмотреть вложение 128225

Если мы используем свойство, нам не нужно будет ставить скобки и оно что-то нам вернёт, например:

project.Variables["result"].Value = instance.CachePath; //в переменную result ляжет путь к кэшу браузера
А вот в методах скобки уже нужны, и они что-то делают. Они тоже могут что-то возвращать после своей работы, но не всегда

instance.Reload(); //перезагрузка браузера
Вот так мы можем с вами очистить кэш (1 строка), куки (2 строка) и прокси (3 строка).

C#:
instance.ClearCache();
instance.ClearCookie();
instance.ClearProxy();
Есть ещё один случай, как мы можем получить объект какого-то класса. Нам его может вернуть свойство или метод другого класса. Формула такая

Название_класса любое_имя = Название_другого_класса.свойство;
Вот так мы получаем доступ к объекту класса Tab от разработчиков ZennoLab

Tab tab = instance.ActiveTab;
Теперь мы можем обращаться по тому имени, которое придумали (tab). Положим в переменную result исходный код страницы (DOM). Код целиком с самого начала

C#:
Tab tab = instance.ActiveTab;
project.Variables["result"].Value = tab.DomText;
Посмотреть вложение 128226
Т.е. свойство DomText класса Tab возвращает нам DOM страницы.

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

И ещё одно - все классы хранятся внутри пространства имён (namespace). Пока условимся, что пространство имён – просто некая коробка для хранения классов. Например, большинство классов от разработчиков ZennoPoster лежат внутри пространства имен под названием "ZennoLab.CommandCenter".

Посмотреть вложение 128227

Давайте подытожим, что мы узнали на текущий момент:
  • В c# есть готовые классы, предлагающие ту или иную функциональность.
  • Чтобы нам что-то сделать, надо получить доступ к объекту класса. Иногда нужно создавать экземпляр класса (через new), иногда мы обращаемся сразу по названию, иногда мы получаем его из свойств и методов другого класса.
  • После этого мы можем использовать свойства и методы этого класса.
  • Также мы уже умеем с вами создавать экземпляры класса Form, instance, Tab. Умеем вызывать некоторые их свойства и методы
#2

Давайте теперь немного разграничим такие понятия как класс, объект, тип данных и переменная.

Класс (class) – это некий чертёж, по которому создаются объекты. Например, если у нас класс человек, то объектом уже будет конкретный Вася Пупкин, Иван Иванов и т.д.

Объект, соответственно, это уже конкретный экземпляр класса.

Переменная – просто некая "коробка", которая хранит данные. Например, мы взяли из списка прокси и нам надо его куда-то положить. Как раз в переменную мы его и положим.

Тип данных же определяет, какого именно типа данные хранятся в переменной. Например, прокси мы будем хранить в строковом типе данных string, а вот возраст уже в числовом типе int.

Запоминать это особо не нужно, так, больше для справки. Это даже не официальные определения. Кому важна точность, лучше погуглить. Позже будете интуитивно всё это понимать. Я вообще постараюсь употреблять поменьше профессиональных терминов, ибо сам в них не особо разбираюсь (помните, я не программист). Главное понять суть, как нам работать.

В C# данные условно делятся на простые (строки, числа, …) и сложные (объекты, структуры, …).

В C# есть следующие простые типы данных:
  • Строка (string). Заключается в двойные кавычки
  • Число (byte, sbyte, short, ushort, int, uint, long, ulong, float, double, decimal)
  • Булевое значение (bool). Принимает только два значения: true (правда) или false (ложь)
  • char. Один символ
Отступление

Прежде чем перейти к рассмотрению простых типов данных, рассмотрим комментарии и вывод информации в лог.

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

Однострочный комментарий обозначает двумя косыми чертами

//я комментарий
Многострочные косой чертой и звездочкой

C#:
/*
Я многострочный комментарий.
Можно писать что угодно пока вы его не закроете
*/
Вывод в лог работает аналогично кубику "Логика" -> "Оповещение". Для этого нам надо воспользоваться классом project и его методом SendInfoToLog(). В качестве параметра он принимает строку (string). О параметрах метода мы поговорим позже.

project.SendInfoToLog("Выводим сообщение в лог ZennoPoster");
Не стоит принимать мои слова на веру, проверяйте всё на практике. Весь код, который вы видите в рамке, вы можете вставлять в кубик c# и смотреть, что происходит. Скопируйте строку выше и вставьте в C# кубик. Появилась ли информация в логе?

P.S. Если кто из старичков вдруг захочет поворчать, что project это вообще не class, а interface, то я в курсе, однако это не играет никакого значения. Сейчас (да и потом) забивать этим голову нет смысла, поэтому я буду называть project именно классом.

P.P.S. Если кто будет проходить уроки в ютубе или читать статьи про c#, то там вы метод SendInfoToLog() не встретите, т.к. он у нас от разработчиков ZennoPoster. В Visual Studio используется Console.WriteLine(), который похож на наш метод. Чтобы вам потестить примеры из статей в project maker, надо просто заменить Console.WriteLine на project.SendInfoToLog.


Переменные

Давайте чтобы в голове не возникала каша разберемся с этим подробнее. Начнем с кубиков. Мы с вами создаем переменные вот так
Посмотреть вложение 128230


И далее помещаем в эту "коробку" уже какую-то информацию (прокси, данные со страницы, пути к файлам и т.д.).

Когда мы работаем с этими переменными, мы вообще не задумываемся о типе данных. Т.е. мы можем класть туда что угодно: числа, строки, true/false, даты и т.д. В c# же нам надо следить за тем типом данных, который мы используем.

Я буду называть эти переменные "переменные уровня проекта". Их можно использовать в кубике c#. Вот так мы можем положить туда значение "Вася":

project.Variables["result"].Value = "Вася";

Т.е. обращение к "переменным уровня проекта" происходит с помощью конструкции

project.Variables["имя_переменной"].Value

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

project.Variables["result"].Value = 123; //error
Чтобы ошибки не было, мы должны цифры заключить в двойные кавычки:

project.Variables["result"].Value = "123";

"Переменная уровня проекта" (которая на самом деле вообще не переменная, но для удобства я называю ее именно так) у нас от разработчиков команды ZennoLab. В обычном c# её нет. Её стоит использовать только тогда, когда вам надо перебросить данные между кубиками. Т.е. если у вас идет кубик c#, в котором надо сохранить какие-то данные, вы можете сохранить их вот в такую переменную. В остальных же случаях мы используем простые переменные, о которых сейчас и поговорим.

Строка (string)

Создаём (инициализируем) переменную с типом string

string myVariable;
Как видим, сначала мы указываем тип переменной, а затем даём ей имя. Имя может быть почти что любым.

После того, как мы её создали, мы можем присвоить ей какое-то значение (т.е. положим в нашу коробку какие-то данные). При повторном обращении к переменной прописывать тип не нужно, иначе будет ошибка.
C#:
//создаём переменную с именем message
string message;
/*

если сделаем так, то получим ошибку, т.к. при повторном обращении указывать тип
уже не нужно(т.е. не надо прописывать string)
string message = "положили какие-то данные";
*/

message = "положили какие-то данные";
message = "а теперь поменяли данные";
P.S. Никогда не забывайте точку с запятой в конце, т.к. она означает окончание инструкции.

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

Знак равно (=) в c# называется знаком присваивания. Как вы уже поняли, он присваивает переменным какое-то значение. Математическое же равно пишется двойным знаком равенства, т.е. ==.

Также мы можем объединить эти две строчки в одну. Т.е. при создании переменной сразу же присвоить ей значение
C#:
//создание строковой переменной с именем mySuperNameVariable (имя может быть любым)
string mySuperNameVariable;

//присваиваем переменной mySuperNameVariable значение "Вася Пупкин"
mySuperNameVariable = "Вася Пупкин";

//а теперь создадим строковую переменную с именем car и сразу же присвоим ей значение
string car = "Audi";
Имена переменных желательно делать осмысленными, чтобы можно было понять, что там внутри. Я так и не научился их нормально именовать, но вам желательно к этому стремиться.

Нельзя создавать переменные с одинаковым именем. Т.е. если вы уже создали

string name = "Вася";
И позже в коде опять создадите переменную с тем же именем
string name = "Петя";
То получите в логе ошибку
Посмотреть вложение 128231

Имена переменных не могут совпадать с зарезервированными словами (например, нельзя давать переменной имя string, ведь так уже обозначается тип), не могут начинаться с цифры и ещё что-то там. Запоминать это не надо, редактор сам подскажет вам, если вы накосячили.
Посмотреть вложение 128232

Осмысленные части в имени переменной принято разделять регистром типа

string firstNameAndLastName = "Вася Пупкин";

И да, C# чувствителен к регистру, поэтому вот эти переменные – это три разных переменных:
C#:
//ошибки не будет, т.к. name и Name или naMe = это всё разные имена
string name = "Вася";
string Name = "Петя";
string naMe = "Коля";
Нельзя менять тип переменной. Т.е. если вы создали переменную

string name = "Вася";
То вы уже не сможете засунуть туда int (число)
name = 123; //приведёт к ошибке

Переменная будет существовать только в рамках одного кубика C#. Т.е. если вы в 1-м кубике C# создадите переменную

string name = "Вася";
А затем создадите новый кубик C# и попробуете к ней обратиться, то будет ошибка, что данной переменной не существует.

Давайте пока вы не запутались ещё раз повторим. По сути вот эти две строки одинаковые

C#:
string message = "сообщение";
project.Variables["message"].Value = "сообщение";
И в первом, и во втором случае мы с вами создали переменную с именем message с типом string и присвоили ей значение "сообщение".

Но к обычной переменной вы можете обращаться только в рамках того кубика, в котором она создана. Она как бы "уничтожается", когда мы переходим на новый кубик c#. К "переменной уровня проекта" же можно обращаться в любом кубике, и она сохранит то значение, которое вы ей присвоили.

Проверьте это на практике. Если мы выполним этот код

project.Variables["message"].Value = "сообщение";
То увидим, что значение изменилось
Посмотреть вложение 128233


Т.е. теперь мы с вами умеем заменять кубик "обработка переменных" c# кодом
Посмотреть вложение 128234
Мы можем объединить значения двух и более переменных в одну с помощью оператора +. Называется это конкатенацией.
C#:
string firstName = "Вася";
string lastName = "Пупкин";
string firstAndLastName = firstName + lastName;
project.SendInfoToLog(firstAndLastName); //получаем в логе " ВасяПупкин"
Аналогично делается и для "переменных уровня проекта" (только не забудьте их сначала создать вручную)

C#:
project.Variables["firstName"].Value = "Вася";
project.Variables["lastName"].Value = "Пупкин";
project.Variables["firstAndLastName"].Value = project.Variables["firstName"].Value + project.Variables["lastName"].Value;
project.SendInfoToLog(project.Variables["firstAndLastName"].Value); //получаем " ВасяПупкин"
Также никто не запрещает нам совмещать "переменные уровня проекта" и обычные строковые переменные, т.к. как я уже говорил, это по сути одно и тоже.

C#:
string firstName = "Вася";
string lastName = "Пупкин";
project.Variables["firstAndLastName"].Value = firstName + lastName;
project.SendInfoToLog(project.Variables["firstAndLastName"].Value); //получаем в логе " ВасяПупкин"

Также вы наверное обратили внимание, что имя и фамилия у нас "слиплись". А как же добавить пробел?

C#:
string firstName = "Вася";
string lastName = "Пупкин";
string firstAndLastName = firstName + " " + lastName + " Андреевич";
project.SendInfoToLog(firstAndLastName); //получаем "Вася Пупкин Андреевич"
Мы можем добавить всё, что хотим, главное использовать строки (т.е. или пишем в двойных кавычках или используем переменную string)

Числовые типы

Итак, смотрите.

double, float, decimal – это всё дроби. Пример написания:

C#:
double chislo = 2.23;
float chislo2 = 2.23f;
decimal chislo3 = 2.23m;
P.S. Помним, что "chislo", "chislo2" – просто имя переменной, которое может быть любым.

В основном вы будете пользоваться double. Decimal используется для операций, где важна точность, т.к. в C# (да и не только в нём) при сложении, вычитании, делении и т.д. может теряться точность в копейках.

int, uint, byte, sbyte, short, ushort, long, ulong – просто числа (без дроби). Пример написания:

C#:
int chislo4 = 25;
byte chislo5 = 3;
Зачем столько типов? Они отличаются размером занимаемой памяти. Так, byte занимает всего 1 байт, в то время как long уже 8 байт. При этом byte может принимать значения от 0 до 255, а long от -9 223 372 036 854 775 807 до 9 223 372 036 854 775 807. Если выйти за эти пределы, то будет ошибка.

Полную таблицу размеров можно загуглить. Нам в принципе всё равно, мы используем int, который принимает значения что-то вроде от -2 миллиардов до 2 миллиардов и занимает 4 байта.

Но что если всё же превысить допустимый лимит? Как вы уже наверное догадались, будет ошибка
Посмотреть вложение 128235


А сможем ли мы вывести наше число в лог? Давайте попробуем
Посмотреть вложение 128236

Как видим, мы получим ошибку. Почему? Метод SendInfoToLog() в качестве параметра принимает у нас только string. При попытке передать любой другой тип мы получаем ошибку. Здесь нужно выполнить преобразование из int в string, но об этом чуть позже. Пока же лишь упомяну, чтобы не было ошибки, мы будем вызывать метод Convert.ToString().

Операции с числами

Плюс, минус, умножить, поделить, остаток от деления, больше, меньше, равно – всё как в математике. Единственное, равно мы пишем вот так:

==

Главное следите, чтобы числа были одного типа.

C#:
int chislo = 2;
int chislo2 = 255;
int result = chislo + chislo2;
project.SendInfoToLog(Convert.ToString(result));
А давайте попробуем сделать тоже самое с типом string

C#:
string chislo = "2";
string chislo2 = "255";
string result = chislo + chislo2;
project.SendInfoToLog(result); //результат: 2255
Уловили разницу? Строки у нас соединяются, а вот числа уже складываются как в математике.

+= означает прибавить к текущему значению

C#:
int chislo = 100;
chislo += 50; //тоже самое, что chislo = chislo + 50;
project.SendInfoToLog(Convert.ToString(chislo)); //результат: 150

! (воскл знак) означает не.

++ означает увеличить значение на один. Называется инкремент

C#:
int chislo = 0;
chislo++;
project.SendInfoToLog(Convert.ToString(chislo)); //получим 1
Можно и уменьшать на 1, используя --
Есть ещё декремент, когда эти знаки ставятся не после переменной, а до. Т.е.

C#:
int chislo = 0;
++chislo;
В чём же тогда разница? Не забивайте пока себе этим голову. Мы это рассмотрим при прохождении циклов (если забуду, то напомните). Особо любопытные могут погуглить. В 99% случаев пользуемся инкрементом.

Boolean

Тип bool принимает два значения: true (правда) или false (ложь). Возвращается оно, когда мы проверяем какое-то условие. Например, существует ли элемент, является ли элемент таким-то и т.д.

Несколько примеров:

C#:
int chislo = 50;
int chislo2 = 100;
bool result = chislo == chislo2;
project.SendInfoToLog(Convert.ToString(result)); //т.к. значения переменных не равны, нам вернётся False

C#:
int chislo = 50;
int chislo2 = 100;
bool result = chislo < chislo2;
project.SendInfoToLog(Convert.ToString(result)); //т.к. 50 действительно меньше чем 100, нам вернется True

C#:
//вспоминаем, что воскл знак означает "не"
int chislo = 50;
int chislo2 = 100;
bool result = chislo != chislo2;
project.SendInfoToLog(Convert.ToString(result)); //т.к. 50 действительно не равно 100, нам вернется True

Char

char нужен для хранения одного символа. Пишется в одинарных кавычках. Пример:

C#:
char symb = '5';
project.SendInfoToLog(Convert.ToString(symb));

Преобразование типов

Мы можем преобразовывать один тип в другой. Для этого нужно пользоваться специальными методами. Например, с помощью метода Convert.ToString() можно преобразовать в строку (любой простой тип). Давайте попробуем

C#:
int chislo = 555;
string strChislo = Convert.ToString(chislo);
project.SendInfoToLog(strChislo); //в лог выводится, ошибок нет, значит всё получилось
А как на счёт Boolean или char? Всё аналогично

C#:
bool variable = true;
string str = Convert.ToString(variable);
project.SendInfoToLog(str);
C#:
char symb = 'a';
string stroka = Convert.ToString(symb);
project.SendInfoToLog(stroka);
P.S. А давайте вспомним, что тут вообще происходит. Смотрите, Convert - это– класс. Только он не от разработчиков ZennoPoster, он у нас из самого c#. Раз это класс, у него есть свойства и методы. ToString() – один из таких методов. В скобках мы указываем параметр. Параметры метода мы ещё не проходили, но уже касались в этом уроке при изучении метода SendInfoToLog().

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


C#:
string strChislo = "123";
int chislo = Convert.ToInt32(strChislo); //для преобразования в int

string strBool = "true";
bool bl = Convert.ToBoolean(strBool); //для преобразования в bool

string strDouble = "2.01";
double d = Convert.ToDouble(strDouble); //для преобразования в double

Ну это основные. Разные short, byte и т.д. пропустим, но эти там тоже все есть.

#3

Продолжим разбираться с классами. Мы уже с вами познакомились с несколькими классами, такими как instance, tab, htmlelement и др.

Все эти классы были сделаны разработчиками ZennoPoster. Именно поэтому спрашивать о них бесполезно на форумах или у chatGpt (если только предварительно не скормить ему справку). Информацию о них нужно искать на форуме zennolab.com или в самой документации (https://help.zennolab.com/en/v7/zennoposter/7.1.4/webframe.html#topic1.html). Для этого в яндексе/гуле пишем что-то вроде «instance.SetwindowSize что делает site:zennolab.com» и читаем.

Однако помимо того, что нам предоставляют разработчики ZennoPoster, внутри самого C# (т.е. в .Net) есть масса собственных классов. Плюс есть масса библиотек, созданных сторонними разработчиками. Плюс вы можете создавать и свои собственные классы. Всё это мы будем изучать позже.

Параметры методов

Опять же для простоты условимся, что аргументы = параметры. Дело в том, что в некоторые методы мы должны передавать аргументы, чтобы они работали, а в некоторые нет. Что это такое? Легче всего объяснить на практике.

instance.ClearProxy();

Этот метод очищает прокси браузера. Видите пустые скобки? Значит данный метод не имеет никаких параметров (аргументов). А теперь посмотрим на метод с параметрами.

instance.SetProxy("127.0.0.1:8888");
https://help.zennolab.com/en/v7/zennoposter/7.1.4/webframe.html#topic377.html

Этот метод устанавливает прокси в браузер. Видите, в скобках мы прописали наш прокси? Это и есть параметр.

Значит запоминаем. Некоторые методы не требуют параметров, а некоторые требуют. Как узнать надо ли их передавать? Можно посмотреть документацию к методу (выше давал ссылки), а можно посмотреть и в самом Project Maker при написании кода. Подсказка появится после того, как вы поставите скобку.
Посмотреть вложение 128238
===
Посмотреть вложение 128239

Обратите внимание, что параметры перечислены через запятую. Сначала указывается тип параметра, а затем его имя (которое может быть любым и не играет роли, это имя просто подсказывает нам, что мы должны прописать).

Если мы возьмём метод SetProxy() (смотри скрин выше), то мы видим, что первым параметром передаем string (вспоминаем прошлый урок), затем bool, bool, bool. Пример:
instance.SetProxy("127.0.0.1:8888", false, true, true, true);
Также увидеть есть ли параметры у метода можно при выборе метода. Когда мы с вами пишем instance и ставим точку, то у нас появляется набор свойств и методов класса instance. Там же дается информация о том, что делает метод (на англ) и имеет ли он параметры.
Посмотреть вложение 128240
Обязательные и необязательные параметры

Методы могут иметь как обязательные, так и необязательные параметры. Что это значит? Если параметр обязательный, а мы его не передали (не прописали), то будет ошибка. Если же параметр необязательный, то мы сможем запустить метод и без него. Например, вышеупомянутый метод

instance.SetProxy("…");

Имеет один обязательный (прокси) и четыре необязательных параметра (использовать ли проксифаер, устанавливать ли таймзону, гео, webrtc). Т.е. первый мы обязаны прописать в любом случае, а остальные по желанию. Понять сколько у метода обязательных параметров можно посмотрев на подсказку. Все необязательные параметры заключены в квадратные скобки.
Посмотреть вложение 128241
Рассмотрим ещё один пример

instance.ClearCookie();
Посмотреть вложение 128242
Данный метод очищает куки в браузере. У него 1 необязательный параметр. При желании мы можем указать те домены, для которых хотим очистить куки.

Необязательные параметры имеют значение по умолчанию. Т.е. если вы ничего не указали, то подставляется то значение, которое решили разработчики. Что это за значение смотрим в документации.

Перегрузки метода

Один и тот же метод может иметь несколько перегрузок. Это означает, что у нас есть несколько возможностей передать параметры.

Вернёмся к нашему методу по установке прокси. Когда мы его прописываем, то в подсказке видим, что он имеет три перегрузки:
Посмотреть вложение 128243

Пощёлкайте по стрелкам, и вы увидите, как будут меняться тип и количество передаваемых параметров. В первой перегрузке мы должны обязательно прописать наш прокси в string и четыре по желанию в bool. Теперь нажмем на стрелку и посмотрим вторую перегрузку:

Посмотреть вложение 128244

Здесь у нас уже два параметра, при этом первый имеет тип ProxySettings, а второй NetworkSettings. Они оба обязательные.

Нажмём на стрелку ещё раз и увидим третью перегрузку:
Посмотреть вложение 128245


Ага, тут у нас уже три обязательных параметра (сразу смотрим на типы, это string-int-string) и 6 необязательных.

Давайте вспомним метод из предыдущего пункта Convert.ToString()
Посмотреть вложение 128246

Как видим, у него вообще 36 перегрузок. Именно поэтому у нас в качестве параметра может быть как int, так и bool, char, uint и многие другие типы.
Тип передаваемых параметров

Прежде чем прописывать какой-то из параметров, нам надо посмотреть на его тип. Обычно это простые типы, пройденные нами в предыдущем уроке (string, int, bool и т.д.), но необязательно.
Посмотреть вложение 128247

Посмотрим на вторую перегрузку этого метода ещё раз
Посмотреть вложение 128248

Мы видим, что тут в качестве 1-го параметра идёт "сложный" тип данных ProxySettings, а в качестве 2-го NetworkSettings.

Соответственно, прежде чем мы сможем их прописать, нам надо будет разобраться, что это за типы и как с ними работать (читаем справку, форум зенно, гугл, chatGpt). Далее их создать, заполнить и затем уже передать.

Возвращаемый тип данных

Методы могут не просто что-то делать, но и что-то возвращать. А возвращать они опять же будут какой-то тип данных (class, если вам так проще). Это мы можем увидеть в подсказке, либо в документации. Давайте вернемся к уже ставшему нами любимому методу по установке прокси. Глядите сюда
Посмотреть вложение 128249

void означает, что метод ничего не возвращает. Значит, он просто что-то делает и на этом всё.

А теперь посмотрим на другой метод

instance.GetCookie();
Посмотреть вложение 128250

Видим, что данный метод возвращает string (значит результат его работы надо положить в переменную с типом string).

C#:
string myCookieBrowser = instance.GetCookie();
return myCookieBrowser;
Возвращать значение необязательно. Т.е. вот такая строка тоже будет работать

instance.GetCookie();
Просто конкретно в данном случае мы не увидим результат работы метода, т.к. его единственным предназначением является возврат кук.

Как и в случае с параметрами возвращаемый тип данных необязательно будет простым. Например вот тут
Посмотреть вложение 128251

Нам вернётся массив IExtension. Т.е. разбираемся, что это, создаем, потом уже присваиваем ей результат работы метода.

#4

Условия if

В любой программе условия используются постоянно. "Если переменная равна тому-то, то делай это", "Если элемент не найден, делай вот это" и т.д. Условие прописывается следующим образом
C#:
int age = 18;
if(age == 18)
{
            project.SendInfoToLog("age действительно 18");
}
Вспоминаем, что равно в C# пишется двойным знаком равно ==. В вышеприведенном коде написано следующее:
  • Первой строкой мы объявили переменную типа int с именем age и присвоили ей значение 18
  • Второй строкой мы говорим "если age равна 18, то выполни то, что находится внутри {}. В данном случае выведи сообщение в лог".
Строк внутри фигурных скобок {} может быть сколько угодно.

Мы также можем прописать, что делать если условие НЕ выполнено.

C#:
int age = 11;

if(age == 18)
{
            project.SendInfoToLog("age действительно 18");
}
else
{
            project.SendInfoToLog("age не равна 18. она равна " + Convert.ToString(age));
}
В данном случае мы написали, что если age равна 18, то выведи в лог " age действительно 18", в ином же случае выведи " age не равна 18. она равна " и значение переменной age. Т.к. age у нас int, а метод SendInfoToLog() принимает string, мы преобразовали её в string с помощью метода Convert.ToString(). Мы также использовали оператор + , чтобы соединить эти две строки.

Ладно, что-то я отвлекаюсь. Всё равно на текст наверное оно не очень воспринимается, на практике ещё закрепим. А пока продолжим.

Мы также можем прописать несколько различных условий, используя else if. Т.е. у нас будет если age == 18, делай то-то, если она равна 20, то делай то-то, если 30, то делай то-то, а если ничему из этого, то делай вот это.

C#:
int age = 20;

if(age == 18)
{
            project.SendInfoToLog("age действительно 18");
}
else if(age == 20)
{
            project.SendInfoToLog("age равна 20");
}
else if(age == 20)
{
            project.SendInfoToLog("age равна 20");
}
else if(age == 30)
{
            project.SendInfoToLog("age равна 30");
}
else
{
            project.SendInfoToLog("age равна " + Convert.ToString(age));
}
Финальный else {] можно было не прописывать, если он вам не нужен.

Помимо знака равно, мы можем использовать и другие, такие как не равно != больше или меньше > <, >= <=.

C#:
int age = 20;

//обратите внимание, что выполнится только первое условие, остальные опустятся
if(age != 18)
{
            project.SendInfoToLog("age действительно НЕ РАВНА 18");
}
else if(age == 20)
{
            project.SendInfoToLog("age равна 20");
}
else if(age == 20)
{
            project.SendInfoToLog("age равна 20");
}
else if(age == 30)
{
            project.SendInfoToLog("age равна 30");
}
else
{
            project.SendInfoToLog("age равна " + Convert.ToString(age));
}

C#:
int age = 10;

if(age >= 18)
{
            project.SendInfoToLog("age действительно НЕ РАВНА 18");
}
else if(age == 20)
{
            project.SendInfoToLog("age равна 20");
}
else if(age < 18)
{
            project.SendInfoToLog("age равна 20");
}
Внутри условий может использоваться любой тип данных, в том числе string, bool и т.д.

C#:
string message ="hello world";
bool flag = true;

if(message == "hello world")
{
            project.SendInfoToLog("message у нас hello world");
}

if(flag == true)
{
            project.SendInfoToLog("flag true");
}

Если у вас то, что в {} занимает только одну строку, их можно не прописывать

C#:
bool flag = true;
if(flag == true) project.SendInfoToLog("можно так");

if(flag == true)
            project.SendInfoToLog("так тоже можно с переносом на след строку");

Когда у нас в скобках идёт bool, можно прописывать сокращенно

C#:
bool flag = true;

//тоже самое что if(flag == true)
if(flag) project.SendInfoToLog("оки");

//тоже самое что if(flag == false)
if(!flag) project.SendInfoToLog("no");
Помимо if-else существует также switch, но его мы рассматривать пока не будем, дабы не забивать голову лишним. Вернёмся к нему позже, когда вы немного "окрепните". А так по идее можно жить и без switch.

Использование "и" и "или" в условиях

Мы можем проверять сразу несколько утверждений. С помощью && задается "и", а с помощью || "или".

C#:
string str = "message";
bool flag = true;

if(str == "message" && flag == true)
{
            project.SendInfoToLog("оба условия выполнены");
}
C#:
string str = "message";
bool flag = true;

if(str == "message" || flag == true)
{
            project.SendInfoToLog("оба условия выполнены");
}
Существует также знак ^. Вернёт true, только если одно из условий является истинным, а другое ложным. На практике я таким не пользовался.

C#:
string str = "message";
bool flag = true;

if(str == "message" ^ flag == false)
{
            project.SendInfoToLog("оба условия выполнены");
}
Видимость переменных

Переменная, объявленная внутри фигурных скобок {}, будет видна только там. Давайте разберемся на практике
Посмотреть вложение 128252

Мы получаем ошибку, код даже не запустится. Посмотрите на описание "The name 'test' does not exist in the current context". [Строка: 9; Cтолбец: 23]" (имя test не существует в данном контексте).

К слову, нумерацию строк мы можем вкл щёлкнув правой кнопкой мыши по полю, где пишем код и выбрав "нумерация строк"
Посмотреть вложение 128253

Также можно вкл в настройках по умолчанию
Посмотреть вложение 128254

Итак, вернёмся к нашей ошибке. Т.к. переменная test была создана внутри {}, то и использовать её можно только там. За пределами её уже нет. Чтобы она была видна за фигурными скобками, надо было её создать до них

C#:
string message ="hello world";
string test = "тестируем видимость переменнной";

if(message == "hello world")
{
            project.SendInfoToLog(test);
}
project.SendInfoToLog(test);
Посмотреть вложение 128255

Переменная будет видна только в рамках одного кубика C#. Смотрите, я создал два кубика
Посмотреть вложение 128256

В "кубике 1" я прописал следующую строчку

string message ="hello world";
Если я теперь попробую в "кубике 2" к ней обратиться, то получу ошибку "переменная message не существует в данном контексте"
Посмотреть вложение 128257

В "кубике 2" уже можно заново создавать переменные с теми же именами. Т.е. если у вас в "кубике 1" есть переменная с именем message, то в "кубике 2" тоже можно её создавать, никакой ошибки не будет
Посмотреть вложение 128258

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

Посмотреть вложение 128260

В [""] указывается имя переменной. Не забудьте только создать её вручную, как вы это раньше всегда делали.

Чтобы постоянно не прописывать эту длинную строку, вы можете нажать правой кнопкой мыши по редактору кода и выбрать там из списка.
Видео по теме - youtube.com/watch?v=EpTRp6deeYI

Списки

У нас есть списки из .Net и списки, созданные разработчиками ZennoPoster. Работают они на 99% одинаково.

Давайте для простоты начнём с зенковского списка. Доступны те же операции, что и в кубике "Операции над списком" – добавление в список, удаление и т.д.

Все строки внутри этого списка имеют тип string.

Первым делом создаём его вручную
Посмотреть вложение 128261


Теперь получим к нему доступ

IZennoList list = project.Lists["Список 1"];
  • Список 1 – это то имя, которое вы задавали, когда создавали список.
  • list – это имя переменной с типом IZennoList. Оно может быть любым
Теперь обращаясь по имени переменной(list), мы можем совершать различные операции с нашим списком (путем вызова методов). Рассмотрим наиболее популярные
  • Add() – добавляет строку в список
  • AddRange() – добавляет данные из другого списка
  • Bind() – привязка файла к списку, если вы это не делали вручную при его создании
  • Clear() – очищает список
  • Contains() – проверяет наличие строки в списке (полное совпадение). Возвращает bool
  • Count – это свойство. Возвращает количество элементов в списке
  • Insert() – позволяет вставить строку в определенное место в списке (куда-то в середину, например)
  • Remove() – удаляет строку по значению
  • RemoveAt() – удаляет строку по номеру (счёт с нуля)
Доступ к конкретному элементу получаем путём указания порядкового номера в []. Обратите внимание, что счёт начинается с нуля (т.е. первый элемент в списке идет под индексом ноль)

C#:
//инициализируем список
IZennoList list = project.Lists["Список 1"];

//очищаем список
list.Clear();

//добавляем элементы в список
list.Add("Вася");
list.Add("Петя");
list.Add("Коля");

//выводим в лог первый элемент списка
string firstStr = list[0];
project.SendInfoToLog(firstStr);

//проверяем наличие в списке
bool checkPety = list.Contains("Петя");
if(checkPety == true)
{
            project.SendInfoToLog("петя есть в списке");
}

//удаляем первый элемент из списка и потом выводим первый элемент в лог
list.RemoveAt(0);
firstStr = list[0];
project.SendInfoToLog(firstStr);

//получаем количество строк в списке
int countList = list.Count;
project.SendInfoToLog("В списке столько строк - " + Convert.ToString(countList));
Посмотреть вложение 128262

Теперь про списки, которые находятся внутри .Net. Это своего рода "виртуальный" список, который будет жить в рамках одного кубика. Аналогию можно провести с переменными. Т.е. если мы берём переменную

project.Variables["number"].Value = "";
То она живёт на протяжении всего проекта в любых кубиках c#. Если же мы берём такую переменную

string message = "";
То она будет существовать только в рамках кубика, где была создана.

Аналогично с нашими списками. Если мы возьмём список IZennoList, то он тоже живёт на протяжении всего проекта. Если же мы берем "виртуальный" список, то он живёт в рамках одного кубика c#. Создаётся он так

List<string> myName = new List<string>();
P.S. myName – это имя, может быть любым.

И далее для него актуальны всё те же методы, что мы прошли для IZennoList.

Зачем он тогда нужен вообще, если данные на пк не сохраняются? Ну например, вы что-то спарсили со страницы в этот список, затем обработали каким-то образом, а потом уже сохраняете на пк с помощью метода File.ReadAllLines(). Этот метод пройдём позже. Плюс тут мы можем использовать не только тип string, но и любые другие, как простые (int, bool, char и т.д.), так и сложные (HtmlElement, mHatch и др.).

Также здесь есть несколько дополнительных методов:
  • Shuffle() – перемешивает элементы списка
  • Sort() – сортирует список
C#:
List<string> myName = new List<string>();

//добавляем элементы в список
myName.Add("Вася");
myName.Add("Петя");
myName.Add("Коля");
myName.Add("Катя");
myName.Add("Надя");
myName.Add("Лена");

//выводим в лог первый элемент списка
string firstStr = myName[0];
project.SendInfoToLog(firstStr);

//проверяем наличие в списке
bool checkPety = myName.Contains("Петя");
if(checkPety == true)
{
            project.SendInfoToLog("петя есть в списке");
}

//удаляем первый элемент из списка и потом выводим первый элемент в лог
myName.RemoveAt(0);
project.SendInfoToLog(myName[0]);

//получаем количество строк в списке
int countList = myName.Count;
project.SendInfoToLog("В списке столько строк - " + Convert.ToString(countList));

//перемешиваем список и выводим в лог 1-й элемент
myName.Shuffle();
project.SendInfoToLog(myName[0]);

//сортируем список по алфавиту и выводим в лог 1-й элемент
myName.Sort();
project.SendInfoToLog(myName[0]);
Видео для закрепления -

Циклы

Циклы нужны для совершения повторяющихся действий. Например, это может быть перебор списка, перебор элементов на странице и т.д. В C# есть циклы for, while, do-while, foreach. Мы рассмотрим только for, чтобы не забивать голову лишним. Без остальных можно жить, но мы всё равно вернёмся к ним позже.

Мы помним, что в списке элементы получаются по индексу

list[0] – первый элемент
list[1] – второй элемент

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

C#:
List<string> myName = new List<string>();

//добавляем элементы в список
myName.Add("Вася");
myName.Add("Петя");
myName.Add("Коля");
myName.Add("Катя");
myName.Add("Надя");
myName.Add("Лена");

//получим кол-во элементов
int count = myName.Count;
int i=0;

//перебираем список
for(i=0;i<count;i = i+1)
{
            string el = myName[i];
            project.SendInfoToLog(el);
}

project.SendInfoToLog("================");

//перемешаем список
myName.Shuffle();

//переберем ещё раз, используя более короткую запись
for(int j=0;j<myName.Count;j++)
{
            project.SendInfoToLog(myName[j]);
}
Иногда нам будет нужно пропустить какую-то строчку в списке. Для этого используется continue. В примере ниже мы пропустим все строчки, которые содержат слово "Вася".

C#:
List<string> myName = new List<string>();

//добавляем элементы в список
myName.Add("Вася");
myName.Add("Петя");
myName.Add("Коля");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Катя");
myName.Add("Надя");
myName.Add("Лена");
myName.Add("Вася Пупкин");
myName.Add("Вася Иванов");
myName.Add("Иванов Вася");

int sch = 0;
for(int i=0;i<myName.Count;i++)
{
            bool checkName = myName[i].Contains("Вася");
            if(checkName == true)
            {
                        continue;
            }
            sch = sch + 1;
            project.SendInfoToLog(myName[i]);
}

project.SendInfoToLog("Количество строк, в которых нет слова Вася - " + Convert.ToString(sch));
Иногда нам надо покинуть цикл, если мы нашли в нём то, что искали. Для этого используется break. Давайте выйдем из цикла сразу же, как обнаружим Надю.

C#:
List<string> myName = new List<string>();

//добавляем элементы в список
myName.Add("Вася");
myName.Add("Петя");
myName.Add("Коля");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Катя");
myName.Add("Надя");
myName.Add("Лена");
myName.Add("Вася Пупкин");
myName.Add("Вася Иванов");
myName.Add("Иванов Вася");

string name = "";
bool flag = false;
for(int i=0;i<myName.Count;i++)
{
            bool checkName = myName[i] == "Надя";
            if(checkName == true)
            {
                        flag = true;
                        name = myName[i];
                        break;
            }
}

if(flag == true)
{
            project.SendInfoToLog("нашли Надю и положили её в переменную name - " + name);
}
else
{
            project.SendInfoToLog("Нади не было в списке");
}

Циклы можно вкладывать "один в другой" (вложенные циклы). Только имя счётчика меняйте.

P.S. Счётчиком называют переменную, с которой начинается цикл. В нашем случае это было int i=0.

Нормальный пример сходу придумать не получается, поэтому просто бредовый приведу вам. На практике ещё встретим такую ситуацию, я думаю.

C#:
List<string> myName = new List<string>();

//добавляем элементы в список
myName.Add("Вася");
myName.Add("Петя");
myName.Add("Коля");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Вася");
myName.Add("Катя");
myName.Add("Надя");
myName.Add("Лена");
myName.Add("Вася Пупкин");
myName.Add("Вася Иванов");
myName.Add("Иванов Вася");

string name = "";
bool flag = false;
for(int i=0;i<myName.Count;i++)
{
            bool checkName = myName[i] == "Надя";
            if(checkName == true)
            {
                        flag=true;
                        name = myName[i];

                        for(int j=0;j<10;j++)
                        {
                                   project.SendInfoToLog("надю нашли. наш j на текущий момент - " + Convert.ToString(j));
                        }
            }
}

if(flag == true)
{
            project.SendInfoToLog("нашли Надю и положили её в переменную name - " + name);
}
else
{
            project.SendInfoToLog("Нади не было в списке");
}

Видео по теме -

Генерация своей ошибки

Мы можем сгенерировать собственную ошибку. Зачем это надо? Например, если какой-то элемент не найден, то возможно нам нет смысла продолжать выполнять наш проект. Для этого генерируем ошибку и идем по красной ветке. При этом в логе будет нормальное описание и номер строки, где была сгенерирована ошибка.

C#:
project.SendInfoToLog("вывод в лог №1");
throw new Exception("моя собственная ошибка");
project.SendInfoToLog("вывод в лог №2");
Посмотреть вложение 128263

Поиск ошибок

В будущем в вашем проекте у вас может быть множество кубиков C#. Имейте ввиду, что ошибка в одном кубике приводит к тому, что все кубики C# перестают работать. Если возникает ошибка первым делом надо определить, в каком именно кубике C# она возникла. Делается это также, как с обычными кубиками.

Давайте создадим несколько кубиков и умышленно забудем поставить точку с запятой в одном из них
Посмотреть вложение 128264


Идём в лог -> жмём правой кнопкой мыши по ошибке -> скопировать айди действия
Посмотреть вложение 128265


Идём в "редактирование" -> "поиск по проекту" (или жмём "ctrl F")
Посмотреть вложение 128266

Вставляем айди и жмём найти. Мы попадём на кубик с ошибкой
Посмотреть вложение 128267


В логе мы видим на какой именно строке ошибка и её описание. Иногда он может косячить и незначительно ошибаться с номером строки
Посмотреть вложение 128268


Иногда номер строки не отображается вовсе. Как в таком случае понять, где ошибка? Обычно, когда это случается, он вам позволит запустить кубик. А ошибка возникнет по ходу выполнения. В этом случае можно выполнить код пошагово. Для этого жмём сюда и у вас появляется красный кружок
Посмотреть вложение 128269

Теперь запускаем кубик и выполняем код построчно
Посмотреть вложение 128270


Так вы сможете дойти до ошибки.

Ещё один вариант – натыкать выводов в лог и посмотреть, какой именно не выведется.

try-catch

Если мы знаем, что в каком-то месте кода может произойти ошибка, и мы хотим её обработать, мы можем использовать специальную конструкцию try-catch.

C#:
try
{
            project.SendInfoToLog("псевдокод");
            //тут 100500 строк кода
            throw new Exception("вдруг где-то возникла ошибка");
}
catch(Exception e)
{
            project.SendInfoToLog("Описание ошибки" - e.Message);
}
finally
{
            project.SendInfoToLog("я выполнюсь в любом случае");
}
В блок try {} мы помещаем код, в котором может возникнуть ошибка (выход по красной ветке).

Если ошибка действительно возникает, выполнение перескакивает в блок catch {} и выполняется код, написанный там.

Если ошибка НЕ возникает, то выполнится только то, что внутри try {}. То, что в catch {}, выполняться не будет.

finally {} выполнится в любом случае, независимо от того, попали мы в catch или нет. finally можно не прописывать вовсе, если он вам не нужен.

Быстрое создание переменных одного типа

Если вам нужно объявить несколько переменных одного типа их можно перечислить через запятую:

C#:
string firstname = "Вася", lastname = "Иванов", job = "Сантехник";
project.SendInfoToLog(firstname);
Интерполяция строк

Ранее мы с вами соединяли строки с помощью оператора +, например:

C#:
string firstname = "Вася", lastname = "Иванов", job = "Сантехник";
string fullInformation = firstname + " " + lastname + " " + job;
project.SendInfoToLog(fullInformation);
Но вы также можете использовать более удобный синтаксис, называемый интерполяцией строк:

C#:
string firstname = "Вася", lastname = "Иванов", job = "Сантехник";
string fullInformation = $"наш рабочий это: {firstname} {lastname}. Его специализация {job}";
project.SendInfoToLog(fullInformation);
Т.е. ставим знак $ перед кавычками, а все переменные для вывода заключаем в {}.

Константы

Мы можем запретить изменять значение переменной, прописав слово const.
const string name = "вася";
Такая переменная называется константой. При попытке поменять значение получаем ошибку
Посмотреть вложение 128271

#5
lock

Когда мы пишем код на C#, мы должны с вами сами контролировать, чтобы разные потоки не могли одновременно обратиться к одному и тому же файлу, иначе можно получить ошибку (вы же не хотите, чтобы одна и та же почта из файла попала сразу к двум-трём потокам?). Это может быть список, таблица, база данных. Для этого существует конструкция lock.

Суть её очень простая. Если какой-то из потоков зашёл внутрь lock {}, то пока он оттуда не выйдет, другие должны будут ждать.

Как это можно написать

C#:
IZennoList list = project.Lists["Список 1"];

lock(SyncObjects.ListSyncer)
{
            //обращение к списку, таблице, бд
  
            string str = list[0];
            list.RemoveAt(0);
}
Обсудим то, что у нас пишется в скобках (SyncObjects.ListSyncer). Это у нас объект, не позволяющий другим потокам залезать внутрь {}. Есть следующие готовые заготовки:
  • SyncObjects.ListSyncer – для списков
  • SyncObjects.TableSyncer – для таблиц
  • SyncObjects.InputSyncer – для буфера обмена
  • SyncObject – общее
Разделение весьма условное. Т.е. никто вам не запрещает использовать SyncObjects.ListSyncer и для таблиц, это просто поделили для удобства.

Мы также можем создавать и свои объекты для лока. Для этого создаем общий код
Посмотреть вложение 128277


И на 33-й строке мы уже видим одну из наших заготовок
Посмотреть вложение 128278

По образу и подобию создаём свои, меняем только имя (SyncObject)
Посмотреть вложение 128279

И теперь можем их использовать в кубике c#, обращаясь таким образом: имя_класса.имя_объекта

C#:
lock(CommonCode.OneLock)
{
            //обращение к списку, таблице, бд
  
            string str = list[0];
            //list.RemoveAt(0);
}
Зачем же создавать собственные объекты для лока и вообще зачем их столько?

Дело в том, что один такой лок

C#:
lock(CommonCode.OneLock)
{
  
}
будет действовать на все участки кода, где вы его используете. Если не ошибаюсь, действие даже может распространяться даже на разные шаблоны (как-нибудь мы это проверим путём эксперимента).

Соответственно, если вы будете использовать какой-то один объект для всех файлов (например тот, что мы написали выше), то при работе в множество потоков у вас может возникнуть нехилая такая "пробка" – очередь из потоков.

Поэтому будет неплохо для каждого файла создавать свой объект для лока. Например, у вас есть файл с прокси. Создайте для него

public static object ProxyLock = new object();
И используйте его. Для файла с ключевыми словами создайте новый

public static object KeyLock = new object();
И используйте его

C#:
lock(CommonCode.KeyLock)
{
            string str = list[0];
}
Для какой-то таблицы свой и т.д. Это не строгое правило. Если в вашем проекте используется не очень много файлов или вы не планируете работать в большое количество потоков, то так заморачиваться не стоит, можно использовать 1-2 лока и успокоиться на этом. Но в ином случае лучше заморочиться.

Работа с таблицами

По тому же принципу, что мы работали со списками, используя IZennoList, мы можем работать с таблицами. Т.е. первым делом вручную создаем таблицу, даём ей имя и привязываем к файлу. Потом работаем с кодом.

Инициализация

IZennoTable table = project.Tables["Таблица 1"];

Добавление строк

C#:
table.ColSeparator = ";"; //задаём разделитель столбцов
string str1 = "вася;пупкин;сантехник"; //добавляемая строка
string str2 = "петя;пчелкин;электрик";
string str3 = "гена;букин;продавец обуви";
table.AddRow(str1);
table.AddRow(str2);
table.AddRow(str3);
Читаем ячейку

C#:
string str = table.GetCell(0, 0); //первая колонка первый столбец (счёт с нуля)
project.SendInfoToLog(str);
Устанавливаем значение в ячейку

table.SetCell(0, 0, "Жора"); //первая строка первый столбец установим "Жора"

Удаляем колонку

table.DeleteColumn(2); //удаляем третью колонку (счёт с нуля)

Удаляем строку

table.DeleteRow(0); //удаляем первую строку

Получаем количество колонок и строк

C#:
int colCount = table.ColCount; //кол-во колонок
int rowCount = table.RowCount; //кол-во строк
Получаем строку

C#:
List<string> list = table.GetRow(0).ToList(); //получаем первую строку
for(int i=0;i<list.Count;i++)
{
            project.SendInfoToLog(list[i]);
}
Очищаем таблицу

table.Clear();
Массивы

Массивы – тоже самое что списки, только имеют они строго определенную длину. Т.е. если мы создали массив из 5 элементов, то изменить это количество мы уже не сможем.

Инициализация массива:

C#:
int[] mass = new int[4];
mass[0] = 12;
mass[1] = 55;
mass[2] = 33;
mass[3] = 23;
Если вам заранее известны значения, которые вы хотите записать, то можно так

int[] mass = new int[] {124,12412,21,57,89};
Используемый тип может быть любым. Хоть string, хоть HtmlElement (и т.д.).

Получение длины массива происходит с помощью свойства Length (не Count как у списков)

int longMass = mass.Length;

Перебор в цикле аналогичен перебору списков

C#:
int[] mass = new int[] {124,12412,21,57,89};
for(int i=0;i<mass.Length;i++)
{
            project.SendInfoToLog(Convert.ToString(mass[i]));
}
Обращение к элементам как у списка, т.е.

int m = mass[0]; //получим первый элемент

Словарь (Dictionary)

Словарь – это коллекция, позволяющая нам хранить данные в формате "ключ значение".

Инициализируем словарь и добавляем значения

C#:
Dictionary<string, string> sl = new Dictionary<string, string>();
sl.Add("Имя", "Вася"); //сначала ключ, потом значение
sl.Add("Фамилия", "Иванов");
Ключ всегда уникальный! Т.е. мы не сможем добавить ещё один ключ "Имя" в этот же словарь, будет ошибка.

Обращение к элементу происходит по ключу, а получаем мы значение этого ключа

C#:
Dictionary<string, string> sl = new Dictionary<string, string>();
sl.Add("Имя", "Вася");
sl.Add("Фамилия", "Иванов");
string str = sl["Фамилия"];
project.SendInfoToLog(str); //Результат: Иванов
Очистка словаря

sl.Clear();

Проверка наличия ключа

C#:
Dictionary<string, string> sl = new Dictionary<string, string>();

sl.Add("Имя", "Вася");
sl.Add("Фамилия", "Иванов")

bool checkName = sl.ContainsKey("Имя");
return checkName; //True
Проверка наличия значения

C#:
Dictionary<string, string> sl = new Dictionary<string, string>();
sl.Add("Имя", "Вася");
sl.Add("Фамилия", "Иванов");

bool checkName = sl.ContainsValue("Василий");
return checkName; //False
Количество элементов в словаре

int count = sl.Count;

Пример перебора словаря

C#:
Dictionary<string, string> sl = new Dictionary<string, string>();
sl.Add("Имя", "Вася");
sl.Add("Фамилия", "Иванов");

for(int i=0;i<sl.Count;i++)
{
            string key = sl.ElementAt(i).Key;
            string value = sl.ElementAt(i).Value;
  
            project.SendInfoToLog($"Ключ: {key}. Значение: {value}");
}
Также добавлю, что на практике применяется довольно часто.

Var

Когда мы используем var, мы говорим компилятору, чтобы он сам подставил тип переменной. Он смотрит на "правую" часть (какой тип там находится) и исходя из этого определяет тип переменной. Примеры

C#:
var str = "строка"; //это у нас будет string
var number = 123; //это будет int
var tab = instance.ActiveTab; //а это Tab
var el = tab.FindElementByXPath("//p", 0); //это HtmlElement
Object

object является базовым для всех типов данных.

Так как все классы в .NET являются производными Object, каждый метод, определенный в Object классе, доступен во всех объектах в системе. Производные классы могут переопределять некоторые из этих методов, включая:

  • Equals: поддерживает сравнения между объектами.
  • Finalize: выполняет операции очистки перед автоматическим восстановлением объекта.
  • GetHashCode: создает число, соответствующее значению объекта для поддержки использования хэш-таблицы.
  • ToString: производит текстовую строку, читаемую человеком, которая описывает экземпляр класса.
Тут надо понимать, что object это не var и не dynamic. Хоть вы и можете положить туда любой тип данных свойства и методы этих типов вам будут недоступны

C#:
object str = "строка";
//str = str.Replace("строка", "123"); - будет ошибка, мы не можем пользоваться методами для string если у нас переменная object
object number = 123;
return str;
Dynamic

dynamic позволяет нам создавать переменную с любым типом. Ключевым тут является то, что это ключевое слово опускает проверку типов во время компиляции. Лучше не использовать и вот почему:

  • У вас не будет подсказок во время написания кода
  • Узнать об ошибке вы сможете только после запуска кода
Пример

C#:
dynamic str = "строка";
str = str.Replace("строка", "123");
dynamic number = 123;
Методы string

Мы рассмотрим наиболее популярные свойства и методы. Это во многом аналогично вот этому кубику
Посмотреть вложение 128280

Все нижеизложенные примеры мы ещё раз посмотрим в видео и также вы найдёте их в шаблоне "4".

Замена

Для этого используем метод Replace(). В качестве 1-го параметра передается старое значение, а в качестве 2-го новое

C#:
//создадим переменную с именем text и присвоим ей значение
string text = "Вася пошёл на прогулку и вернулся только в 20:00";

//выведем в лог значение
project.SendInfoToLog(text);

//заменим фразу "на прогулку" на "гулять" и выведем в лог
string newText = text.Replace("на прогулку", "гулять");
project.SendInfoToLog(newText);
//сохраним новое значение в переменной уровня проекта "name"
project.Variables["name"].Value = newText;
Перевод в верхний и нижний регистр

Делается методами toUpperCase() и toLowerCase().

C#:
//создадим новую переменную с именем "toUpperVariable" и переведем в верхний регистр и выведем в лог
string toUpperVariable = text.ToUpper();
project.SendInfoToLog(toUpperVariable);


//а теперь в нижний регистр
string toLowerVariable = text.ToLower();
project.SendInfoToLog(toLowerVariable);
Проверка существования

С помощью метода Contains() мы можем проверить, есть ли в нашей переменной нужное слово. Обратите внимание, что метод возвращает тип bool, а значит и результат мы должны положить в переменную с типом bool. Но вот незадача. Чтобы вывести результат в лог (с помощью project.SendInfoToLog()) мы должны передать string, а не bool. Что же делать? Нужно превратить bool в string и только потом выводить в лог. Не переживайте, эту тему мы ещё затронем позже

C#:
bool checkText = text.Contains("Вася");
string checkTextToString = Convert.ToString(checkText);
project.SendInfoToLog(checkTextToString);
Количество символов

Метод Count() позволяет нам посчитать количество символов в тексте. Также можно это сделать и с помощью свойства Length. Обратите внимание, что этот метод возвращает уже int, а значит и результат мы ложим в переменную с типом int. Ну, а далее чтобы вывести в лог или сохранить результат в переменную уровня проекта, нам надо превратить int в string аналогично тому, как делали выше

C#:
//узнаём количество символов в тексте с помощью Count и выводим в лог
int countSymbolsInText = text.Count();
string strCountSymbolInText = Convert.ToString(countSymbolsInText);
project.SendInfoToLog(strCountSymbolInText);

//узнаём количество символов в тексте и выводим в лог с помощью свойства Length
int countSymbolsInTextLengthProperties = text.Length;
string strCountSymbolInTextLengthProperties = Convert.ToString(countSymbolsInTextLengthProperties);
project.SendInfoToLog(strCountSymbolInTextLengthProperties);
Чем начинается или заканчивается строка

Метод StartsWith() позволяет проверить, начинается ли строка с нужной фразы. Метод EndsWith() проверяет, заканчивается ли строка указанный нами фразой. Оба метода в результате выполнения возвращают bool

C#:
//узнаем начинается ли переменная с Вася пошёл
bool startText = text.StartsWith("Вася пошёл");
string strStartText = Convert.ToString(startText);
project.SendInfoToLog("Результат выполнения начинается ли текст с фразы 'Вася пошёл' - " + strStartText);

//узнаем заканчивается ли переменная на 20:00
bool endText = text.EndsWith("Вася пошёл");
string strEndText = Convert.ToString(endText);
project.SendInfoToLog("Результат выполнения чем заканчивается текст - " + strEndText);
Где находится искомая фраза

Метод IndexOf() позволяет узнать, с какого символа начинается указанная нами фраза. Если её вообще нет, то вернётся -1.

C#:
//откуда начинается искомая фраза (прогулк)
int checkWalk = text.IndexOf("прогулк");
string strCheckWalk = Convert.ToString(checkWalk);
project.SendInfoToLog($"искомое слово начинается с {strCheckWalk} символа");

Вставка фразы

Метод Insert() позволяет вставить новую строку в переменную. 1-м параметром передаем с какого символа вставляем (int), а вторым саму фразу (string).

C#:
//вставляем фразу начиная с 5-го символа
string newTextAddPhrase = text.Insert(5, "Пупкин ");
project.SendInfoToLog(newTextAddPhrase);
Удаление части строки

Метод Remove() позволяет удалить часть строки. 1-м параметром передаем откуда удаляем (int), 2-м параметром сколько удаляем (int)

C#:
//удаляем часть строки
string deleteText = text.Remove(0, 4);
project.SendInfoToLog(deleteText);
Удаление пробелов в начале и конце

Метод Trim() удалит пробелы в начале и конце строки. Метод TrimStart() удалит только в начале, а TrimEnd() только в конце.

C#:
//удаляем пробелы
string strForEdit = "     строка с пробелами    ";
project.SendInfoToLog("так выглядит строка с пробелами - " + strForEdit);
string newStrForEdit = strForEdit.Trim();
project.SendInfoToLog("убрали пробелы - " + newStrForEdit);

Ещё пара слов

Переменные можно объединять друг с другом с помощью оператора +. Это называют конкатенацией строк. Также можно пользоваться и другим способом, продемонстрированном ниже

C#:
//объявляем переменные
string str1 = "первая строка";
string str2 = "вторая строка";

//объединяем
string str3 = str1 + str2;
project.SendInfoToLog("результат - " + str3);

//добавим пробел
string str4 = str1 + " " + str2;
project.SendInfoToLog("добавили пробел, чтобы нормально смотрелось");

//объединяем 2-м способом
string str5 = $"ещё можно вот так соединять: {str1} {str2}";
project.SendInfoToLog(str5);
Я бы ещё вот что хотел до вас донести. Вот смотрите метод project.SendInfoToLog() у нас в качестве параметра принимает строку. Мы можем как писать её напрямую

project.SendInfoToLog("пишем текст");
Можем указать переменную (но только в string)

C#:
string str = "пишем текст";
project.SendInfoToLog(str);
Можем производить конкатенацию прямо там

C#:
string str = "пишем текст";
project.SendInfoToLog(str + " добавляем что угодно".Insert(0, " И ещё добавим "));
#6

Порядок передачи параметров в методы

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

C#:
Tab tab = instance.ActiveTab;
tab.Navigate(referrer: "google.ru", url: "ya.ru");

Цепочка вызовов

Мы можем существенно сокращать объём написания кода, используя так называемую "цепочку вызовов". Давайте я лучше объясню на примерах. Вот так мы совершаем переход на сайт

C#:
Tab tab = instance.ActiveTab;
tab.Navigate("ya.ru");
Тут мы сначала положили Tab в переменную и далее к ней обращаемся.

Но мы можем обращаться и сразу без создания переменной таким образом

instance.ActiveTab.Navigate("ya.ru");
Поскольку instance.ActiveTab возвращает нам объект Tab, компилятор сам понимает, что теперь он работает с Tab и предоставляет нам свойства и методы для работы с ним.

Эта цепочка может быть сколь угодно длинной

string str = instance.ActiveTab.MainDocument.FindElementByXPath("/html/body/div/div[@class]", 0).FullTagName;
  • instance.ActiveTab возвращает Tab. Мы получили доступ к его свойствам и методам
  • MainDocument является свойством класса Tab. Он возвращает тип Document, так что теперь мы получили доступ к нему
  • У класса Document есть метод FindElementByXpath() (да, этот метод доступен не только в классе Tab, но и в классе Document) который вернул нам тип HtmlElement
  • У HtmlElement есть свойство FullTagName, который возвращает тип string. Именно поэтому мы положили результат в тип string.
На всякий случай. Вышеизложенную строку можно целиком расписать вот так (делайте как вам удобнее, но первая время я советую расписывать целиком):

C#:
Tab tab = instance.ActiveTab;
Document doc = tab.MainDocument;
HtmlElement el = doc.FindElementByXPath("/html/body/div/div[@class]", 0);
string str = el.FullTagName;
Цикл While

Аналогичен циклу for, но записывается чуть иначе. Переберем список

C#:
List<string> list = new List<string>()
{
            "Вася",
            "Петя",
            "Коля"
};

int i=0; //счётчик
while(i < list.Count)
{
            project.SendInfoToLog(list[i]);
            i++; //увеличение счётчика
}

Иногда while используется для создания бесконечных циклов. Только не забывайте предусмотреть выход из него, чтобы программа не зависла

C#:
List<string> list = new List<string>()
{
            "Вася",
            "Петя",
            "Коля"
};

int i=0; //счётчик
string str = "";
while(true)
{
            try {
                        str = list[i];
            }
            catch{
                        project.SendInfoToLog("перебрали список");
                        break;
            }
            project.SendInfoToLog(list[i]);
            i++; //увеличение счётчика
}
Цикл do while

В отличии от while, тот код, что находится в do {} будет выполнен в любом случае, даже если условие в скобках ложно.

C#:
List<string> list = new List<string>()
{
            "Вася",
            "Петя",
            "Коля"
};

int i=0; //счётчик
string str = "";
do
{
            project.SendInfoToLog(list[i]);
            i++; //увеличение счётчика
}
while(i < list.Count);
Цикл foreach

Удобен для перебора коллекций, но не всегда его можно использовать

C#:
List<string> list = new List<string>()
{
            "Вася",
            "Петя",
            "Коля"
};

foreach(string s in list)
{
            project.SendInfoToLog(s); //в s автоматически кладется сначала первый элемент списка, затем 2-й и т.д. пока не переберем всё
}
Условие switch

Всё, что делается на switch, можно сделать на if-else. А вот в обратную сторону это не работает. Но в некоторых случаях код читатется легче именно со switch.

C#:
List<string> list = new List<string>()
{
            "Вася",
            "Петя",
            "Коля"
};


switch(list[0]) //тут переменная, значение которой проверяем
{
            case "Вася": //тут возможное значение переменной
                        project.SendInfoToLog("Первый элемент в списке Вася"); //тут действия, если значение совпало. можно сколько угодно строк
                        break; //так мы говорим, что действия закончены
            case "Петя":
                        project.SendInfoToLog("Первый элемент в списке Петя");
                        break;
            case "Коля":
                        project.SendInfoToLog("Первый элемент в списке Коля");
                        break;
            case "Михаил":
            case "Василий":
                        project.SendInfoToLog("Михаил или Василий");
                        break;
            default: //если не совпало никакое из значений
                        project.SendInfoToLog("первый элемент в списке неизвестен");
                        break;
}
Практика по пройденным темам:

#10
Введение в общий код


Для чего вам общий код

Давайте я сначала объясню вам чем вам может быть полезен общий код. Разберем пример клика по элементу на c# (код составлен для стр https://ya.ru/).

C#:
HtmlElement el = null;
Tab tab = instance.ActiveTab;
Random r = new Random();

for (int i = 0; i<30; i++)
{
    //пробуем отыскать элемент
    el = tab.FindElementByXPath("//div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]", 0);

    //если найден, пауза и выходим из цикла
    if (!el.IsVoid)
    {
        Thread.Sleep(r.Next(1000, 3000));
        break;
    }

    //выход по ошибке, если элемент не появился
    if (i == 29) throw new Exception("не дождались появления элемента //div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]");

    //пауза 1 сек
    project.SendInfoToLog("ждём появления элемента //div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]");
    Thread.Sleep(1000);
}

//клик
tab.FullEmulationMouseMoveToHtmlElement(el);
Thread.Sleep(r.Next(300, 1000));
tab.FullEmulationMouseClick("left", "click");

//ожидание загрузки и пауза
if (tab.IsBusy) tab.WaitDownloading();
Thread.Sleep(r.Next(1000, 3000));
Вам не кажется, что слишком дохрена кода для одного действия? Но что если я скажу вам, что тот же самый код можно будет вызвать всего одной строчкой вроде:

browser.Click(project, instance, "//div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]");
Вот именно этому мы с вами и научимся. Помимо создания своей библиотеки вы также можете выносить в общий код и логику проекта. Правда, делать это грамотно я вас научить не смогу, но понимание того, как это делается, вы получите.

Библиотеки
Откроем ссылки из GAC. Здесь мы с вами видим те пространства имен (библиотеки), что уже используются в проекте. Т.е. в данном случае у нас добавлено System, System.Core и т.д. Можно зайти на microsoft, вбить это название в поиск и ознакомиться, что там есть.

linkGac.gif
Кнопкой "удалить" можно убрать ненужные. Кнопкой "добавить" можно добавить новые. Давайте нажмём её. Тут мы с вами видим все доступные нам библиотеки (1), их версию (2) и путь к ним (3)

linksbor.png
Чтобы в ваш проект добавить какую-то из этих библиотек, надо нажать по ней два раза кнопкой мыши (или выделить и нажать "ок"). Для примера добавим System.Net. Видим, что она появилась в общем списке

systemNetAdd.gif
Но это далеко не все доступные нам библиотеки. Если мы нажмём на кнопку обзор и зайдём в папку с ZennoPoster, то увидим множество dll, которые также можно добавить. Для примера добавим Newtonsoft.json, которая нужна для работы с JSON

newtonsoftadd.gif
Помимо того, что тут есть, мы можем добавлять и сторонние библиотеки. Закидывать и надо в папку ExternalAssemblies, ну а далее точно также жмём "обзор" и добавляем. Что при этом важно учесть:
  • У нас в ZennoPoster используется .Net Framework 4.6.2, в то время как весь мир сидит уже на .Net. Поэтому прежде чем добавлять библиотеку, убедитесь, что добавляемая библиотека совместима с .Net Framework 4.6.2 (т.е. ищите старые версии библиотек)
  • Сами dll можно искать как где-то на форумах, так и скачивать с Nuget, используя Visual Studio (позже будет видео).

Знакомимся с общим кодом
Добавляем общий код

obshcode.png
В самом верху у нас прописаны директивы using. Это подключенные пространства имён. Можно обойтись и без них, но тогда вам придётся писать полный путь при обращении к классам (имя_пространства_имен.название_класса.название_метода()). Например, чтобы нам вызвать File.Move(...), нам надо будет написать System.IO.File.Move().

Возможно вы также заметили вверху две вкладки: "общий код" (это где мы сейчас находимся) и "директивы using".

dirUsing.png
Так вот, вкладка "директивы using" нужна именно для кубиков c#. Т.е. если вы добавили какую-то библиотеку и не хотите прописывать полный путь начиная с пространства имен, вам надо будет прописать директиву using именно в этой вкладке.

Вернёмся к общему коду. После директив using мы с вами видим namespace ZennoLab.OwnCode {}. Вспоминаем, что namespace (пространство имён) по сути просто коробка для классов. Далее у нас идёт сам класс public class CommonCode{}, внутри которого мы уже можем создавать свои свойства и методы

Давайте мы с вами переместим наш код клика, написанный в кубике c# в общий код. Для этого внутри класса создаём метод

method.png
Наш метод состоит из модификатора доступа (public), возвращаемого значения (void), имени (Click) и параметров (IZennoPosterProjectModel project, Instance instance)
  • Модификаторы доступа нужны для того, чтобы понять, где этот метод можно будет использовать. Написав public мы сказали, что этот метод будет доступен везде (в том числе и кубике c#). Бывает также internal (почти тоже самое, что public), protected (в наследуемых классах) и private (только в этом классе, т.е. в кубике c# он уже доступен не будет)
  • Методы могут не возвращать значений (пишем void), а могут и вернуть абсолютно любой тип (тогда указываем его. например string, int, bool, HtmlElement и т.д.). Мы это уже проходили.
  • Имя может быть почти любым. Действуют те же правила, что и для имён переменных. Публичные (public) методы принято называть с большой буквы, а приватные (private) с маленькой
  • Про параметры вы тоже уже знаете. Объясню, зачем мы с вами прописали IZennoPosterProjectModel project, Instance instance. Дело в том, что по умолчанию в общем коде объекты project и instance недоступны. Если мы хотим их использовать, нам надо их "перекинуть" из кубика c# в общий код. Позже ещё коснёмся этой темы. А пока же просто имейте ввиду, что вам надо прописать эти параметры, если хотите в своём методе воспользоваться project или instance
Теперь переносите код внутрь фигурных скобок
Посмотреть вложение 128282

Нам осталось научиться вызывать наш метод из кубика c#. Для этого нам надо создать экземпляр данного класса, а затем обратиться к нашему методу Click(), не забыв передать project и instance в качестве параметров

C#:
CommonCode myname = new CommonCode();
myname.Click(project, instance);
Как видим, в этот раз нам понадобилось всего 2 строчки вместо 40.

Возвращаемое значение методов
Вспоминаем наши первые уроки. Методы могут как возвращать какое-то значение, так и нет. Если метод ничего не возвращает, мы пишем void. Если же да, то пишем тип, который он возвращает. А в самом коде в самом конце мы должны прописать return возвращаемая_переменная. В предыдущем примере мы с вами написали метод клика, вот только логика ожидания элемента зашита прямо в него. Это не очень хорошо, т.к. ожидание элемента нужно не только при совершении клика, но и при вводе текста, при парсинге и т.д. Давайте вынесем этот код в отдельный метод WaitElement(). Создаем новый метод с этим именем

C#:
public HtmlElement WaitElement(IZennoPosterProjectModel project, Instance instance, string xpath)
{
    HtmlElement el = null;
    Tab tab = instance.ActiveTab;
    Random r = new Random();

    for (int i = 0; i<30; i++)
    {
        //пробуем отыскать элемент
        el = tab.FindElementByXPath("//div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]", 0);

        //если найден, пауза и выходим из цикла
        if (!el.IsVoid)
        {
            Thread.Sleep(r.Next(1000, 3000));
            break;
        }

        //выход по ошибке, если элемент не появился
        if (i == 29) throw new Exception("не дождались появления элемента //div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]");

        //пауза 1 сек
        project.SendInfoToLog("ждём появления элемента //div[@data-hydration-id]/a[contains(@href, 'ya.ru' )]");
        Thread.Sleep(1000);
    }
    return el;
}
Обратите внимание, что возвращаемый тип у нас HtmlElement, т.к. именно его нам возвращает метод FindElementByXpath(...). Также мы добавили параметр методу с типом string, чтобы передавать xpath путь элемента

Теперь мы можем удалить лишний код из метода Click и прописать туда метод WaitElement. Я также добавил параметр xpath, чтобы мы могли передавать xpath путь до элемента

click.png
C#:
public void Click(IZennoPosterProjectModel project, Instance instance, string xpath)
{
    HtmlElement el = null;
    Tab tab = instance.ActiveTab;
    Random r = new Random();

    el = WaitElement(project, instance, xpath);

    //клик
    tab.FullEmulationMouseMoveToHtmlElement(el);
    Thread.Sleep(r.Next(300, 1000));
    tab.FullEmulationMouseClick("left", "click");

    //ожидание загрузки и пауза
    if (tab.IsBusy) tab.WaitDownloading();
    Thread.Sleep(r.Next(1000, 3000));
}
Параметры методов
Вы можете передавать сколько угодно параметров какого угодно типа. Мы также можем передавать необязательные параметры, которые будут иметь значение по умолчанию. Единственное, идти они должны только после обязательных параметров. Давайте "прокачаем" наш метод Click, добавив возможность выбирать, будем ли мы эмулировать мышку и будем ли мы брать паузы до и после кликов

C#:
public void Click(IZennoPosterProjectModel project, Instance instance, string xpath, bool emulationMouse, bool sleep=true)
{
    HtmlElement el = null;
    Tab tab = instance.ActiveTab;
    Random r = new Random();

    el = WaitElement(project, instance, xpath);

    //клик
    if(emulationMouse)
    {
        tab.FullEmulationMouseMoveToHtmlElement(el);
        Thread.Sleep(r.Next(300, 1000));
        tab.FullEmulationMouseClick("left", "click");
    }
    else
    {
        el.Click();
    }


    //ожидание загрузки и пауза
    if(sleep)
    {
        if (tab.IsBusy) tab.WaitDownloading();
        Thread.Sleep(r.Next(1000, 3000));
    }

}
Приватные методы
Выше я упомянал про то, что методы с модификатором public доступны везде, а private только в своём классе. Смотрите, сейчас наш метод WaitElement() доступен как в текущем классе, так и в любом другом (в том числе кубике c#). Давайте убедимся в этом

Посмотреть вложение 128283
Но по сути зачем нам публичный метод WaitElement? Едва ли мы будем использовать его в кубике c#. Поэтому давайте поменяем public на private и убедимся, что он больше недоступен в кубике c#

private.png privateWait.png
К слову, если вы вообще не пропишите модификатор доступа для метода, то он будет считаться private. А если вы не пропишите модификатор доступа для класса, то он будет считаться internal

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

Бывают ситуации, что нам нужно вернуть несколько значений из метода. В таком случае можно использовать вот такую штуку

Общий код

C#:
public void test(out string result1, out string result2)
{
    //любая логика

    //по ходу кода
    result1 = "Возвращаемое значение 1";

    //любая логика

    //по ходу кода
    result2 = "Возвращаемое значение 2";
}
кубик c#

C#:
CommonCode myname = new CommonCode();
string res1=string.Empty, res2=string.Empty;

myname.test(out res1, out res2);
project.SendInfoToLog($"{res1}\n{res2}");
Т.е. нам надо
  1. В методе параметры передать с использованием out
  2. вернуть эти значения по ходу выполнения кода, просто присвоив им нужное значение
  3. В кубике c# (или другом методе) создать переменные
  4. когда будем вызывать метод, мы прописываем их с использованием out
Конструктор

У классов есть такая штука, как конструктор. В нём происходит инициализация объекта, т.е. мы можем с вами прямо при создании экземляра класса присвоить значения каким-то переменным, выполнить проверки и т.д. Конструктор – это как метод, который выполняется сразу при создании экземпляра класса.

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

C#:
public class CommonCode
    {
        /// <summary>
        /// Lock this object to mark part of code for single thread execution
        /// </summary>
        public static object SyncObject = new object();
    
        IZennoPosterProjectModel project;
        Instance instance;
        Tab tab;
    
        public CommonCode(IZennoPosterProjectModel _project, Instance _instance)
        {
            project = _project;
            instance = _instance;
            tab = instance.ActiveTab;
        }
    
        /// <summary>
        /// Метод перехода на страницу
        /// </summary>
        /// <param name="url">куда переходим</param>
        public void Navigate(string url)
        {
            tab.Navigate(url);
            tab.WaitDownloading();
        }

        // Insert your code here
    }
В кубике c# же инициализация класса теперь будет выглядеть следующим образом:

C#:
CommonCode com = new CommonCode(project, instance);
com.Navigate("zennolab.com");
Поля и свойства

Полями в общем коде называют обычные переменные. Их принято делать только приватными. Это означает, что пользоваться ими мы сможем только в том классе, в котором их создали. Т.е. например, в кубике c# вы их не увидите. В примере выше полями у нас являются

C#:
IZennoPosterProjectModel project;
Instance instance;
Tab tab;
Но ничто не мешало добавить нам и другие переменные вроде:

C#:
string name;
int age;
Если мы попробуем посмотреть на них из кубика c#, то ничего не увидим:
Посмотреть вложение 128284
Чтобы они стали доступными, нам нужно добавить модификатор public.

C#:
public string name;
public int age;
Посмотреть вложение 128285

Посмотреть вложение 128286

Но так делать не рекомендуется.

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

Свойства "работают в связке" с приватными полями.

Давайте вместо поля age создадим свойство Age (публичные члены класса принято называть с большой буквы, а приватные с маленькой) и сделаем так, чтобы нельзя было поставить его меньше нуля.

C#:
int age;
    
public int Age
{
    get
    {
        if(age < 0) age = 0; //это наша проверка
        return age; //а тут мы возвращаем результат
    }
    set
    {
        age = value; //означает присвоить устанавливаемое значение
    }
}
Теперь давайте поработаем со свойством Age в кубике c#

Посмотреть вложение 128287
Как видите, когда мы присвоили 25, мы и получили 25. Но если мы присваиваем -5, то получаем 0 (а не -5).

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

public string Job {get;set;}

И теперь можно пользоваться им в кубике c# как обычной переменной.
Посмотреть вложение 128288
Заключение

По общему коду я пробежался довольно поверхностно и возможно кого-то запутал. Вам надо просто начать его использовать, и вы начнете потихоньку "въезжать". Без его освоения полноценно писать на C# нет смысла, но юзать какие-то сниппеты можно.

Чтобы не удлинять статью ещё больше, я вам в отдельном файле прикрепил шпаргалку по ZennoPoster API и некоторым распространенным классам .Net. Там есть примеры кода, которые можно вставлять в кубик c# и смотреть, что происходит. Пользуйтесь.

Надеюсь, эта статья была кому-то полезна и теперь c# стал для вас чуточку проще, чем до её прочтения. Всех благ.

Видео - https://disk.yandex.ru/i/aoN7IKEtaRWhAg
Постарайтесь пожалуйста хотя бы на спойлеры поразбивать, а то не удобно читать
 
  • Спасибо
Реакции: samsonnn и radv

volody00

Client
Регистрация
06.09.2016
Сообщения
929
Благодарностей
1 018
Баллы
93
Бред полный, не правда!
Ну от вас, товарищ samsonn, иного комментария я и не ждал. Но всё же отвечу. Я не работал с vs от слова совсем, так что может ты и прав. Мой вывод основывался на 2 моментах:
  • Я никогда не видел, чтобы кто-то совмещал vs и кубики
  • Я неоднократно видел темы, где люди пишут код в vs, а потом перетаскивают его в общий код, потому что с кубиком vs не работает нормально
Если продолжишь читать, найдёшь ещё 100500 моментов, до которых можно докопаться. Статья не для тебя.
Постарайтесь пожалуйста хотя бы на спойлеры поразбивать, а то не удобно читать
Учту на будущее. Тебе тоже читать смысла нет, разве что как samsonn искать косяки. Я бы сам у тебя поучился
 

radv

Client
Регистрация
11.05.2015
Сообщения
3 820
Благодарностей
2 030
Баллы
113
  • Спасибо
Реакции: samsonnn

Sherminator

Client
Регистрация
10.09.2021
Сообщения
1 299
Благодарностей
701
Баллы
113
Ну статья может и норм, особо не вчитывался, но на форуме видимо традиция сложилась, когда кто то въезжает в C# он считает должным статью на конкурс выкатить на эту тему, тут уже 100500 таких статей, хотя это наверное самая длинная)
 

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 802
Благодарностей
1 473
Баллы
113
хотя это наверное самая длинная)
Оформленна не правильно, сделана в виде одной простыни, по этому такая длинная. Выше просили, чтоб ТС сел и переделал, разделил на части, и убрал под споллер каждую часть, но пока от ТС полный игнор.
 

radv

Client
Регистрация
11.05.2015
Сообщения
3 820
Благодарностей
2 030
Баллы
113
Оформленна не правильно, сделана в виде одной простыни, по этому такая длинная. Выше просили, чтоб ТС сел и переделал, разделил на части, и убрал под споллер каждую часть, но пока от ТС полный игнор.
ну впереди еще отбор статей и голосование. :-)
 
  • Спасибо
Реакции: samsonnn

S10n4eg

Client
Регистрация
25.06.2014
Сообщения
268
Благодарностей
41
Баллы
28
Спасибо автору за статью, как раз хотел изучать C#. Только пожалуйста не удаляйте ее
 

Quazar4ik

Client
Регистрация
23.03.2018
Сообщения
2
Благодарностей
0
Баллы
1
Здоровья тебе, добрый человек. Отлично ввел в тему, особенно когда знания рваные как по ZP так и по C#. А вместе оно дополняет.
Жаль не было этой статьи год назад, половину граблей уже сам прошел.
 

DV_

Client
Регистрация
21.08.2020
Сообщения
79
Благодарностей
19
Баллы
8
83 000 знаков o_O
 

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)