Dynamic language runtime

Введение в адаптивный дизайн

Последнее обновление: 03.05.2016

Сейчас все большее распространение находят различные гаджеты — смартфоны, планшеты, «умные часы» и другие устройства, которые позволяют выходить в интернет, просматривать содержимое сайтов. По некоторым оценкам уже чуть ли не половина интернет-траффика генерируется подобными гаджетами, разрешение экрана которых отличается от разрешения экранов стандартных компьютеров. Подобное распространение гаджетов несет новые возможности по развитию веб-сайтов, привлечения новых посетителей, продвижению информационных услуг и т.д. Но вместе с тем появляются и новые проблемы.

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

На заре распространения мобильных телефонов пользователи могли через телефон по протоколу WAP получать доступ к специальным wap-сайтам, которые были написаны на языке wml — языке на основе xml, похожем на html. К примеру, простейшая веб-страница на этом языке разметке могла иметь следющий код:

<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml" >
<wml>
  <card id="main" title="WapSite">
    <p mode="wrap">Простейшая страница на языке WML.</p>
  </card>
</wml>

Визуально она выглядела так:


Однако развитие самих гаджетов, их возможностей привело к тому, что сейчас мобильные телефоны представляют куда большие возможности по получению и отображению содержимого сайтов, а в написании подобных сайтов используется те же HTML5 и CSS3, что и для обычных сайтов.

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

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

Хотя нередко до сих пор можно встретить ситуацию, когда для сайта создается отдельная мобильная версия, часто с префиксом m, например, m.vk.com. Однако концепция адаптивного становится все более распространенной и доминирующей.

Тестирование адаптивного дизайна

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

Например, в Google Chrome надо перейти в меню Дополнительные инструменты -> Инструменты разработчика. После октрытия панели разработчика в начале меню самой панели можно нажать на иконку мобильного телефона, и после этого можно будет эмулировать отображение страницы на различных устройствах — от небольших телефонов до широкоформатных телевизоров:

В данном случае отображается веб-страница как она бы выглядела на устройстве Samsung Galaxy S III. Но при желании можно выбрать другое устройство, либо даже создать эмуляцию какого-то нового устройства, которого нет во встроенном списке.

Подобные инструменты есть и в других современных веб-браузерах. Например, в Mozilla Firefox для их открытия надо перейти в меню Разработка -> Адаптивный дизайн

НазадВперед

Сокрытие

Последнее обновление: 29.07.2018

В прошлой теме было рассмотрено определение и переопределение виртуальных методов. Другим способом изменить функциональность метода, унаследованного от базового класса, является сокрытие (shadowing / hiding).

Фактически сокрытие представляет определение в классе-наследнике метода или свойства, которые соответствует по имени и набору параметров методу или свойству базового класса. Для сокрытия членов класса применяется ключевое слово new. Например:

class Person
{
	public string FirstName { get; set; }
	public string LastName { get; set; }
	public Person(string firstName, string lastName)
	{
		FirstName = firstName;
		LastName = lastName;
	}

	public void Display()
	{
		Console.WriteLine($"{FirstName} {LastName}");
	}
}

class Employee : Person
{
	public string Company { get; set; }
	public Employee(string firstName, string lastName, string company)
			: base(firstName, lastName)
	{
		Company = company;
	}
	public new void Display()
	{
		Console.WriteLine($"{FirstName} {LastName} работает в {Company}");
	}
}

Здесь определен класс Person, представляющий человека, и класс Employee, представляющий работника предприятия. Employee наследует от Person все свойства и методы. Но в классе Employee кроме унаследованных свойств есть также и собственное свойство Company, которое хранит название компании. И мы хотели бы в методе Display выводить информацию о компании вместе с именем и фамилией на консоль. Для этого определяется метод Display с ключевым словом new, который скрывает реализацию данного метода из базового класса.

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

Используем эти классы в программе в методе Main:

class Program
{
	static void Main(string[] args)
	{
		Person bob = new Person("Bob", "Robertson");
		bob.Display();		// Bob Robertson

		Employee tom = new Employee("Tom", "Smith", "Microsoft");
		tom.Display();		// Tom Smith работает в Microsoft

		Console.ReadKey();
	}
}

Консольный вывод программы:

Bob Robertson
Tom Smith работает в Microsoft

Подобным обазом мы можем организовать сокрытие свойств:

class Person
{
	protected string name;
	public string Name
	{
		get { return name; }
		set { name = value; }
	}
}
class Employee : Person
{
	public new string Name
	{
		get { return "Employee " + base.Name; }
		set { name = value; }
	}
}

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

Более того мы даже можем применять сокрытие к переменным и константам, также используя ключевое слово new:

class ExampleBase
{
	public readonly int x = 10;
	public const int G = 5;
}
class ExampleDerived : ExampleBase
{
	public new readonly int x = 20;
	public new const int G = 15;
}

НазадВперед

Что такое MySQL. Установка сервера

Последнее обновление: 04.05.2018

MySQL представляет систему управления реляционными базами данных (СУБД). На сегодняшний день это одна из самых популярных систем управления базами данных.

Изначальным разработчиком данной СУБД была шведская компания MySQL AB. В 1995 году она выпустила первый релиз MySQL. В 2008 году компания MySQL AB была куплена компание Sun Microsystems, а в 2010 году уже компания Oracle поглотила Sun и тем самым приобрела права на торговую марку MySQL. Поэтому MySQL на сеголняшней день развивается под эгидой Oracle.

Текущей актуальной версией СУДБ является версия 8.0, которая вышла в январе 2018 года.

MySQL обладает кроссплатформенностью, имеются дистрибутивы под самые различные ОС, в том числе наиболее популярные версии Linux, Windows, MacOS.

Установка MySQL

После выбора версии нажмем на кнопку «Go to Download Page», и нас перенаправит на страницу загрузки дистрибутива. Здесь можно выбрать либо онлайн-загрузчик, либо полный пакет инсталятора. Можно выбрать любой:

После загрузки запустим инсталлятор. Вначале нам отобразится окно с лицензионным соглашением, которое нужно принять:

После принятия лицензионного соглашения будет предложено выбрать тип установки. Выберем тип Full и нажмем на кнопку Next:

Далее на следующем этапе может отобразится следующее окно, если какие-то дополнительные компоненты отсутствуют в системе:

В данном случае программа установки показывает, что у меня не установлен Python 2.7. Поскольку эти компоненты не важны, нажимаем Next.


Затем на этапе установки инсталлятор отобразит весь список устанавливаемых компонентов. У меня он выглядит так:

Чтобы выполнить установку всех компонентов, нажмем кнопку Execute.

После того, как все компоненты будут установлены, нажмем кнопку Next.

Далее надо будет указать тип сервера. Выберем настройку по умолчанию Standalone MySQL Server / Classic MySQL Replication

Далее будет предложено установить ряд конфигурационных настроек сервера MySQL. Оставим настройки соединения и порта по умолчанию:

На следующем шаге будет предложено установить метод аутентификации. Оставим настройки по умолчанию:

Затем на следующем окне прогаммы установки укажем какой-нибудь пароль, и запомним его, так как он потом потребуется при подключении к серверу MySQL:

Следующее окно позволяет настроить дополнительные плагины и расширения. Начиная с версии 5.7 в MySQL доступен X Protocol, который представляет новый способ взаимодействия с хранилищем данных. Эту опцию необязательно отмечать. В данном случае я ее отмечу:

И на следующем экране необходимо применить все ранее установленные конфигурационные настройки, нажав на кнопку Execute:

После применения конфигурационных настроек сервер MySQL будет полностью установлен и сконфигурирован, и мы сможем с ним работать.

НазадВперед

Частичные классы и методы

Последнее обновление: 16.07.2018

Классы могут быть частичными. То есть мы можем иметь несколько файлов с определением одного и того же класса, и при компиляции все эти определения будут скомпилированы в одно.

Например, определим в проекте два файла с кодом

Не столь важно как эти файлы будут называться. Например, PersonBase.cs и PersonAdditional.cs

В одном из этих файлов (без разницы в каком именно) определим следующий класс:

public partial class Person
{
	public void Move()
	{
		Console.WriteLine("I am moving");
	}
}

А в другом файле определим следующий класс:

public partial class Person
{
	public void Eat()
	{
		Console.WriteLine("I am eating");
	}
}

Таким образом, два файла в проекте содержит определение одного и того же класса Person, которые содержат два разных метода. И оба определенных здесь класса являются частичными. Для этого они определяются с ключевым словом partial.

Затем мы можем использовать все методы класса Person:

class Program
{
	static void Main(string[] args)
	{
		Person tom = new Person();
		tom.Move();
		tom.Eat();

		Console.ReadKey();
	}
}

Частичные методы

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

Например, изменим выше определенные классы Person. Первый класс:

public partial class Person
{
	partial void DoSomethingElse();

	public void DoSomething()
	{
		Console.WriteLine("Start");
		DoSomethingElse();
		Console.WriteLine("Finish");
	}
}

Второй класс:

public partial class Person
{
	partial void DoSomethingElse()
	{
		Console.WriteLine("I am reading a book");
	}
}

В первом классе определен метод DoSomethingElse, который вызывается в методе DoSomething. Причем на момент определения первого класса неизвестно, что представляет собой метод DoSomethingElse. Тем не менее мы знаем список его параметров и может вызвать в первом классе.

Второй класс уже непосредственно определяет тело метода DoSomethingElse.

При этом частичные методы не могут иметь модификаторов доступа — по умолчанию они все считаются приватными. Также частичные методы не могут иметь таких модификаторов как . Хотя допустимы статические частичные методы.


Кроме того, частичные методы не могут возвращать значения, то есть они всегда имеют тип void. И также они не могут иметь out-параметров.

Поскольку частичные методы всегда приватные, то мы не сможем иx вызвать напрямую в программе вне классов, где они определены. Поэтому обычно они вызываются через другие доступные методы как в случае выше через метод DoSomething:

Person tom = new Person();
tom.DoSomething();

НазадВперед

Реализация интерфейсов в базовых и производных классах

Последнее обновление: 23.06.2020

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

interface IMovable
{
	void Move();
}
abstract class Person : IMovable
{
	public abstract void Move();
}
class Driver : Person
{
	public override void Move()
	{
		Console.WriteLine("Шофер ведет машину");
	}
}

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

interface IAction
{
	void Move();
}
class BaseAction
{
	public void Move()
	{
		Console.WriteLine("Move in BaseAction");
	}
}
class HeroAction : BaseAction, IAction
{
}

Здесь класс HeroAction реализует интерфейс IAction, однако для реализации метода Move из интерфейса применяется метод Move, унаследованный от базового класса BaseAction. Таким образом, класс HeroAction может не реализовать метод Move, так как этот метод уже определен в базовом классе BaseAction.

Следует отметить, что если класс одновременно наследует другой класс и реализует интерфейс, как в примере выше класс HeroAction, то название базового класса должно быть указано до реализуемых интерфейсов:

Изменение реализации интерфейсов в производных классах

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

Первый вариант — переопределение виртуальных/абстрактных методов:

interface IAction
{
	void Move();
}
class BaseAction : IAction
{
	public virtual void Move()
	{
		Console.WriteLine("Move in BaseAction");
	}
}
class HeroAction : BaseAction
{
	public override void Move()
	{
		Console.WriteLine("Move in HeroAction");
	}
}

В базовом классе BaseAction реализованный метод интерфейса определен как виртуальный (можно было бы также сделать его абстрактным), а в производном классе он переопределен.

При вызове метода через переменную интерфейса, если она ссылается на объект производного класса, будет использоваться реализация из производного класса:

BaseAction action1 = new HeroAction();
action1.Move();            // Move in HeroAction

IAction action2 = new HeroAction();
action2.Move();             // Move in HeroAction

Второй вариант — сокрытие метода в производном классе:

interface IAction
{
	void Move();
}
class BaseAction : IAction
{
	public void Move()
	{
		Console.WriteLine("Move in BaseAction");
	}
}
class HeroAction : BaseAction
{
	public new void Move()
	{
		Console.WriteLine("Move in HeroAction");
	}
}

Также используем эти классы:

BaseAction action1 = new HeroAction();
action1.Move();            // Move in BaseAction

IAction action2 = new HeroAction();
action2.Move();             // Move in BaseAction

Так как интерфейс реализован именно в классе BaseAction, то через переменную action2 можно обратиться только к реализации метода Move из базового класса BaseAction.

Третий вариант — повторная реализация интерфейса в классе-наследнике:

interface IAction
{
	void Move();
}
class BaseAction : IAction
{
	public void Move()
	{
		Console.WriteLine("Move in BaseAction");
	}
}
class HeroAction : BaseAction, IAction
{
	public new void Move()
	{
		Console.WriteLine("Move in HeroAction");
	}
}

В этом случае реализации этого метода из базового класса будет игнорироваться:

BaseAction action1 = new HeroAction();
action1.Move();            // Move in BaseAction

IAction action2 = new HeroAction();
action2.Move();             // Move in HeroAction

HeroAction action3 = new HeroAction();
action3.Move();             // Move in HeroAction

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

Четвертый вариант: явная реализация интерфейса:

interface IAction
{
	void Move();
}
class BaseAction : IAction
{
	public void Move()
	{
		Console.WriteLine("Move in BaseAction");
	}
}
class HeroAction : BaseAction, IAction
{
	public new void Move()
	{
		Console.WriteLine("Move in HeroAction");
	}
	void IAction.Move()
	{
		Console.WriteLine("Move in IAction");
	}
}

В этом случае для переменной IAction будет использоваться явная реализация интерфейса IAction, а для переменной HeroAction по прежнему будет использоваться неявная реализация:

BaseAction action1 = new HeroAction();
action1.Move();            // Move in BaseAction

IAction action2 = new HeroAction();
action2.Move();             // Move in IAction

HeroAction action3 = new HeroAction();
action3.Move();             // Move in HeroAction

НазадВперед

Явная реализация интерфейсов

Последнее обновление: 07.10.2019

Кроме неявного применения интерфейсов, которое было рассмотрено в прошлой статье, сушествует также явная реализация интерфейса. При явной реализации указывается название метода или свойства вместе с названием интерфейса, при этом мы не можем использовать модификатор public, то есть методы являются закрытыми:

interface IAction
{
	void Move();
}
class BaseAction : IAction
{
	void IAction.Move()
	{
		Console.WriteLine("Move in Base Class");
	}
}

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

static void Main(string[] args)
{
	BaseAction action = new BaseAction();
	((IAction)action).Move();	// необходимо приведение к типу IAction

	// или так
	IAction action2 = new BaseAction();
	action2.Move();
	
	Console.ReadKey();
}

В какой ситуации может действительно понадобиться явная реализация интерфейса? Например, когда класс применяет несколько интерфейсов, но они имеют один и тот же метод с одним и тем же возвращаемым результатом и одним и тем же набором параметров:

class Person : ISchool, IUniversity
{
    public void Study()
    {
        Console.WriteLine("Учеба в школе или в университете");
    }
}

interface ISchool
{
    void Study();
}

interface IUniversity
{
    void Study();
}

Класс Person определяет один метод , создавая одну общую реализацию для обоих примененных интерфейсов. И вне зависимости от того, будем ли мы рассматривать объект Person как объект типа ISchool или IUniversity, результат метода будет один и тот же.

Чтобы разграничить реализуемые интерфейсы, надо явным образом применить интерфейс:

class Person : ISchool, IUniversity
{
    void ISchool.Study()
    {
        Console.WriteLine("Учеба в школе");
    }
    void  IUniversity.Study()
    {
        Console.WriteLine("Учеба в университете");
    }
}

Использование:

static void Main(string[] args)
{
    Person p = new Person();

    ((ISchool)p).Study();
    ((IUniversity)p).Study();
	
    Console.Read();
}

Другая ситуация, когда в базовом классе уже реализован интерфейс, но необходимо в производном классе по-своему реализовать интерфейс:

interface IAction
{
	void Move();
}
class BaseAction : IAction
{
	public void Move()
	{
		Console.WriteLine("Move in BaseAction");
	}
}
class HeroAction : BaseAction, IAction
{
	void IAction.Move()
	{
		Console.WriteLine("Move in HeroAction");
	}
}

Несмотря на то, что базовый класс BaseAction уже реализовал интерфейс IAction, но производный класс по-своему реализует его. Применение классов:

HeroAction action1 = new HeroAction();
action1.Move();            // Move in BaseAction
((IAction)action1).Move(); // Move in HeroAction

IAction action2 = new HeroAction();
action2.Move();             // Move in HeroAction

Модификаторы доступа

Члены интерфейса могут иметь разные модификаторы доступа. Если модификатор доступа не public, а какой-то другой, то для реализации метода, свойства или события интерфейса в классах и структурах также необходимо использовать явную реализацию интерфейса.

interface IMovable
{
	protected internal void Move();
	protected internal string Name { get; set; }
	delegate void MoveHandler();
	protected internal event MoveHandler MoveEvent;
}
class Person : IMovable
{
	// явная реализация свойства - в виде автосвойства
	string IMovable.Name { get; set; }

	// явная реализация события - дополнительно создается переменная
	IMovable.MoveHandler _moveEvent;
	event IMovable.MoveHandler IMovable.MoveEvent
	{
		add => _moveEvent += value;
		remove => _moveEvent -= value;
	}

	// явная реализация метода
	void IMovable.Move()
	{
		Console.WriteLine("Person is walking"); 
		_moveEvent();
	}
}
class Program
{
	static void Main(string[] args)
	{
		IMovable mov = new Person();
		// подписываемся на событие
		mov.MoveEvent += () => Console.WriteLine("IMovable is moving");
		mov.Move();
	}
}

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


НазадВперед

Элемент head и метаданные веб-страницы

Как правило, одним из первых элементов html-документа является элемент head, задача которого состоит в установке метаданных страницы и ряда сопроводительной информации. Метаданные содержат информацию о html-документе.

Заголовок

Для установки заголовка документа, который отображается на вкладке браузера, используется элемент title

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Элемент title</title>
	</head>
	<body>
		<p>Содержание документа HTML5</p>
	</body>
</html>

Элемент base

Элемент позволяет указать базовый адрес, относительно которого устанавливаются другие адреса, используемые в документе:

<!DOCTYPE html>
<html>
	<head>
		<base href="content/">
		<meta charset="utf-8">
		<title>Элемент base</title>
	</head>
	<body>
		<a href="newpage.html">Перейти</a>
	</body>
</html>

Хотя для ссылки в качестве адреса указана страница newpage.html, но фактически ее адресом будет content/newpage.html. То есть в одной папке с текущей страницей должна быть подпапка content, в которой должен находится файл newpage.html

Можно также указывать полный адрес:

<base href="http://www.microsoft.com/">

В это случае ссылка будет вести по адресу http://www.microsoft.com/newpage.html

Элемент meta

Элемент meta определяет метаданные документа.

Чтобы документ корректно отображал текст, необходимо задать кодировку с помощью атрибута charset. Рекомендуемой кодировкой является utf-8:

<meta charset="utf-8">

При этом надо помнить, что указанная элементе кодировка должна совпадать с кодировкой самого документа. Как правило, текстовый редактор позволяет указать кодировку документа. Если мы хотим ориентироваться на utf-8, то в настройках текстового редактора надо выбирать UTF-8 w/o BOM. Например, выбор кодировки в Notepad++:

Элемент также имеет два атрибута: и content. Атрибут содержит имя метаданных, а — их значение.

По умолчанию в HTML определены пять типов метаданных:

  • application name: название веб-приложения, частью которого является данный документ

  • author: автор документа

  • description: краткое описание документа

  • generator: название программы, которая сгенерировала данный документ

  • keywords: ключевые слова документа

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

Добавим в документ ряд элементов meta:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<base href="content/">
		<title>Элемент title</title>
		<meta name="description" content="Первый документ HTML5">
		<meta name="author" content="Bill Gates">
	</head>
	<body>
		<a href="newpage.html">Содержание документа HTML5</a>
	</body>
</html>

НазадВперед


С этим читают