.NET Разработчик pinned «Hello, world! В этих ваших интернетах всякие тренеры личностного роста и прочие спецы по мотивации говорят, что бороться с прокрастинацией и не бросать начатое проще, если хотя бы записываешь прогресс в дневник. А ещё лучше, если делишься им с друзьями. Я…»
День пятнадцатый. #CodeComplete
2. Именование
1. Имена переменных
Обычно имена переменных, которые недостаточно конкретны, чтобы быть использованы только для одной цели в процедуре, это плохие имена.
- Имя переменной должно быть существительным, описывающим реальную сущность
- Не используйте префиксы, обозначающие тип переменной или отделяющие поля класса от локальных переменных (“
- Делайте имена переменным настолько конкретными, насколько это возможно
- Чем очевиднее, тем лучше
- Чем ближе имя к сущности реального мира, тем обычно оно более полезно
2. Именование методов
- Описывайте в имени всё, что делает метод (либо разбейте его на несколько)
- Делайте имена настолько длинными, насколько можно
- Бессмысленные или неопределённые глаголы (
- Приближайте имена к необходимому уровню абстракции, чтобы скрыть детали реализации метода
- Делайте имена методов настолько высокоуровневыми (близкими бизнес-логике или к реальному миру), насколько это возможно
3. Соглашения об именовании
- утвердите соглашение о согласованном наименовании стандартных операций, например,
- избегайте ненужных вариаций: сокращения, непонятные аббревиатуры, смесь языков и т.п. (
2. Именование
1. Имена переменных
Обычно имена переменных, которые недостаточно конкретны, чтобы быть использованы только для одной цели в процедуре, это плохие имена.
- Имя переменной должно быть существительным, описывающим реальную сущность
- Не используйте префиксы, обозначающие тип переменной или отделяющие поля класса от локальных переменных (“
__...
”, “m_...
”, “s_...
”, “i_...
” и т.п.)- Делайте имена переменным настолько конкретными, насколько это возможно
- Чем очевиднее, тем лучше
- Чем ближе имя к сущности реального мира, тем обычно оно более полезно
2. Именование методов
- Описывайте в имени всё, что делает метод (либо разбейте его на несколько)
- Делайте имена настолько длинными, насколько можно
- Бессмысленные или неопределённые глаголы (
Calc
, HandleClass
, ProcessInput
и т.п.) – тревожный знак- Приближайте имена к необходимому уровню абстракции, чтобы скрыть детали реализации метода
- Делайте имена методов настолько высокоуровневыми (близкими бизнес-логике или к реальному миру), насколько это возможно
3. Соглашения об именовании
- утвердите соглашение о согласованном наименовании стандартных операций, например,
user.GetID()
или user.ID.Get()
- то же касается стандартных аббревиатур и языковых вариаций (color
/colour
)- избегайте ненужных вариаций: сокращения, непонятные аббревиатуры, смесь языков и т.п. (
totalCount
/ttlCnt
/itog
, firstName
/fName
/firstNm
/FN
/imya
).День шестнадцатый. #ЗаметкиНаПолях
Использование using
Все мы привыкли к использованию ключевого слова
Директива
1. Наиболее распространённый вариант использования уже упоминался выше. Нет необходимости каждый раз писать полное имя типа с пространством имён, например:
Оператор using упрощает работу с объектами которые реализуют интерфейс
Использование using
Все мы привыкли к использованию ключевого слова
using
в начале наших файлов для упрощения обращения к типам в определённом пространстве имён. Но у него есть и другие интересные варианты использования.Директива
1. Наиболее распространённый вариант использования уже упоминался выше. Нет необходимости каждый раз писать полное имя типа с пространством имён, например:
using System.Text;2. Для упрощённого обращения к статическим методам класса можно использовать
…
var sb = new StringBuilder();
using static
, например:using static System.Console;3. Использование псевдонимов (алиасов) для пространств имён или типов. Например, для упрощения использования обобщённых типов (в самой директиве, правда, все имена придётся написать полностью):
using static System.Math;
…
WriteLine(Sqrt(3*3 + 4*4));
using System;Оператор
using DateTimeList = System.Collections.Generic.List<System.DateTime>;
…
var dtl = new DateTimeList {DateTime.MinValue, DateTime.Now, DateTime.MaxValue};
Оператор using упрощает работу с объектами которые реализуют интерфейс
IDisposable
. Интерфейс имеет один метод Dispose()
, который используется для освобождения ресурсов, использованных объектом. При использовании using
не обязательно явно вызывать Dispose()
для объекта.using (SqlConnection conn = new SqlConnection()) {При этом компилятор сгенерирует приблизительно следующий код:
…
}
SqlConnection conn = new SqlConnection();
try {
…
}
finally {
if (conn != null)
((IDisposable)conn).Dispose();
}
👍2
День семнадцатый. #Оффтоп
Перевёл статью для канала CodeBlog “Топ-7 вещей необходимых разработчику ПО”. Немного добавлю от себя. Мне изначально понравился подход автора, который не стал углубляться в банальности, типа, «вам надо изучить алгоритмы» или «вы обязательно должны знать, как работать с указателями», или, что особенно доставляло в вузе, «изучение программирования хорошо бы начинать с ассемблера, а потом переходить к высокоуровневым языкам» (WTF???)
В статье же упор делается на условия работы. Как наиболее эффективно использовать доступные вам инструменты, чтобы не только быстро и качественно делать свою работу, но и получать от неё удовольствие. Про себя могу отметить, что попадаю почти под все пункты :) Хотя, пришёл к этому далеко не сразу. Удобство хорошего кресла и просторного рабочего стола я оценил всего лишь года три назад, когда захотели дома сделать спальню из моего кабинета, а меня выселили в другую комнату. Я заодно прикупил себе и кресло с большим компьютерным уголком. А начинал я работу, сидя буквально на обычном деревянном стуле за старым советским столом-книжкой (олды должны помнить такие), который к тому же ещё и ходил ходуном, если на него облокотиться. Идею двух мониторов тоже подсмотрел у коллег, которые ими активно пользовались. Хотя поначалу у меня одним монитором был экран ноута, а вторым старый 15-дюймовый монитор 4:3 с максимальным разрешением 1024х768.
Должен отметить, что мне откровенно повезло с работодателем. И железо, и софт постоянно обновляются по инициативе работодателя. На данный момент мой рабочий ноутбук – это 4х-ядерный Intel Core i7, 16Gb оперативной памяти и 2 SSD диска на 1Tb и 2Tb и 2 монитора по 24”. Вы не представляете, как быстро привыкаешь к хорошему! Когда я прихожу к знакомым помочь с компьютером, я часто ловлю себя на том, что запускаю 3 или 4 окна браузера сразу. Просто потому, что тыкаю на ярлык, и ничего не происходит. На моём ноуте это значит, что я просто промахнулся или не нажал кнопку мыши, поэтому я на автомате тыкаю ещё раз, и ещё раз. А оказывается, просто компу нужно время, чтобы запустить чёртов браузер!
Почитал комментарии в контакте под статьёй. Конечно, там попеняли на то, что хрен ты чего от наших работодателей добьёшься. Да, согласен. Но здесь остаётся только посочувствовать и пожелать налегать на последний пункт статьи - книги и сертификаты - и доказывать, что вы этого достойны. Хороших всем работодателей, интересных задач, а главное, получайте удовольствие от работы, ведь для этого вы и выбрали карьеру программиста, не так ли?
https://shwanoff.ru/top-7-things-a-software-developer-needs/
Перевёл статью для канала CodeBlog “Топ-7 вещей необходимых разработчику ПО”. Немного добавлю от себя. Мне изначально понравился подход автора, который не стал углубляться в банальности, типа, «вам надо изучить алгоритмы» или «вы обязательно должны знать, как работать с указателями», или, что особенно доставляло в вузе, «изучение программирования хорошо бы начинать с ассемблера, а потом переходить к высокоуровневым языкам» (WTF???)
В статье же упор делается на условия работы. Как наиболее эффективно использовать доступные вам инструменты, чтобы не только быстро и качественно делать свою работу, но и получать от неё удовольствие. Про себя могу отметить, что попадаю почти под все пункты :) Хотя, пришёл к этому далеко не сразу. Удобство хорошего кресла и просторного рабочего стола я оценил всего лишь года три назад, когда захотели дома сделать спальню из моего кабинета, а меня выселили в другую комнату. Я заодно прикупил себе и кресло с большим компьютерным уголком. А начинал я работу, сидя буквально на обычном деревянном стуле за старым советским столом-книжкой (олды должны помнить такие), который к тому же ещё и ходил ходуном, если на него облокотиться. Идею двух мониторов тоже подсмотрел у коллег, которые ими активно пользовались. Хотя поначалу у меня одним монитором был экран ноута, а вторым старый 15-дюймовый монитор 4:3 с максимальным разрешением 1024х768.
Должен отметить, что мне откровенно повезло с работодателем. И железо, и софт постоянно обновляются по инициативе работодателя. На данный момент мой рабочий ноутбук – это 4х-ядерный Intel Core i7, 16Gb оперативной памяти и 2 SSD диска на 1Tb и 2Tb и 2 монитора по 24”. Вы не представляете, как быстро привыкаешь к хорошему! Когда я прихожу к знакомым помочь с компьютером, я часто ловлю себя на том, что запускаю 3 или 4 окна браузера сразу. Просто потому, что тыкаю на ярлык, и ничего не происходит. На моём ноуте это значит, что я просто промахнулся или не нажал кнопку мыши, поэтому я на автомате тыкаю ещё раз, и ещё раз. А оказывается, просто компу нужно время, чтобы запустить чёртов браузер!
Почитал комментарии в контакте под статьёй. Конечно, там попеняли на то, что хрен ты чего от наших работодателей добьёшься. Да, согласен. Но здесь остаётся только посочувствовать и пожелать налегать на последний пункт статьи - книги и сертификаты - и доказывать, что вы этого достойны. Хороших всем работодателей, интересных задач, а главное, получайте удовольствие от работы, ведь для этого вы и выбрали карьеру программиста, не так ли?
https://shwanoff.ru/top-7-things-a-software-developer-needs/
👍5
День восемнадцатый. #CodeComplete
3. Процесс Программирования с Псевдокодом
При проектировании сложных или объёмных методов сложно написать метод от начала до конца из головы. В этих случаях помогает процесс программирования с псевдокодом. Он состоит из нескольких этапов:
1. Напишите, что должен делать каждый блок кода на естественном языке, используя комментарии (это будет псевдокод).
Комментарии должны быть на достаточно высоком уровне, чтобы не дублировать то, что будет написано в коде, а объяснять, что делает блок кода. Например, вместо:
используйте
3. Напишите код каждого блока под каждым блоком комментариев.
Применяйте ППП рекурсивно, если нужно. Например, если блок написан слишком общими словами, которые сложно сразу перевести в код, примените ППП к этому блоку (рассмотрите возможность вынести этот блок в отдельный метод).
4. Оставьте псевдокод в виде комментариев.
Преимущества ППП:
- Упрощает изначальное написание кода
- Упрощает обзор кода
- Приводит к лучше организованному коду
- Автоматизирует процесс добавления комментариев
- Упрощает возвращение к работе, если вас прервали на середине
- Сокращает количество ошибок, поскольку процесс позволяет (вынуждает) вас больше думать о правильном проектировании метода
3. Процесс Программирования с Псевдокодом
При проектировании сложных или объёмных методов сложно написать метод от начала до конца из головы. В этих случаях помогает процесс программирования с псевдокодом. Он состоит из нескольких этапов:
1. Напишите, что должен делать каждый блок кода на естественном языке, используя комментарии (это будет псевдокод).
Комментарии должны быть на достаточно высоком уровне, чтобы не дублировать то, что будет написано в коде, а объяснять, что делает блок кода. Например, вместо:
// для каждого i меньше длины массива objectArr
используйте
// перебираем все объекты массива в цикле2. Проверьте весь псевдокод метода. Добавьте более детальные комментарии там, где это необходимо.
3. Напишите код каждого блока под каждым блоком комментариев.
Применяйте ППП рекурсивно, если нужно. Например, если блок написан слишком общими словами, которые сложно сразу перевести в код, примените ППП к этому блоку (рассмотрите возможность вынести этот блок в отдельный метод).
4. Оставьте псевдокод в виде комментариев.
Преимущества ППП:
- Упрощает изначальное написание кода
- Упрощает обзор кода
- Приводит к лучше организованному коду
- Автоматизирует процесс добавления комментариев
- Упрощает возвращение к работе, если вас прервали на середине
- Сокращает количество ошибок, поскольку процесс позволяет (вынуждает) вас больше думать о правильном проектировании метода
👍2
День девятнадцатый. #ЗаметкиНаПолях
Обобщения
Обобщения – это механизм многократного использования алгоритма. Разработчик описывает алгоритм, но не указывает типы данных, с которыми тот работает. Применяя алгоритм, другой разработчик должен указать конкретные типы данных. Например, обобщённый список определяется как List<T>. Здесь переменная T, указывающая на тип называется параметром типа. В объявлении обобщённого типа её можно использовать в любом месте, где указывается, тип данных. Например:
- в параметре метода
Microsoft рекомендует называть параметры типа
Преимущества обобщений:
- защита исходного кода: для использования обобщённого алгоритма не нужен доступ к исходному тексту алгоритма;
- безопасность типов: компилятор и CLR обеспечивают, чтобы в алгоритме использовались лишь объекты, совместимые с указанным типом данных;
- более простой и понятный код: из-за того, что компилятор обеспечивает безопасность типов уже на этапе компиляции, требуется меньше операций приведения типов, а такой код проще писать и поддерживать;
- повышение производительности: не требуется лишних операций приведения типов, для структур не требуется упаковки/разупаковки, что ускоряет работу кода.
Наиболее частым видом использования обобщённых типов являются коллекции, такие как связанные списки, хэш-таблицы, стеки, очереди, деревья и т.п. Операции, вроде добавления и удаления элементов коллекции, выполняются примерно одинаково вне зависимости от типа хранимых данных. В большинстве случаев, когда требуется класс коллекции, рекомендуется использовать классы, определённые в библиотеке классов .NET.
При создании ваших собственных обобщённых типов, важно учитывать следующие моменты:
1. Какие типы обобщать в параметры типа. Обычно, чем больше типов вы параметризуете, тем более гибким становится ваш код. Но слишком обобщённый тип может привести к коду, который будет трудно читать и понять другим разработчикам.
2. Какие ограничения, если они нужны, применить к параметрам типа (об ограничениях в следующих постах). Хорошим тоном будет применить максимально возможные ограничения, которые всё равно позволят вам использовать требуемые типы. Например, если обобщённый тип подразумевает использование только ссылочных типов, добавьте ограничение
3. Обдумайте обобщённое поведение в базовом классе и подклассах.
4. Нужно ли реализовать один или больше обобщённых интерфейсов. Например, если вы разрабатываете класс, который будет использоваться в качестве элемента обобщённой коллекции, вам может потребоваться реализовать интерфейс вроде
Продолжение следует…
Источники:
- Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 12.
- https://docs.microsoft.com/ru-ru/dotnet/csharp/programming-guide/generics/index
Обобщения
Обобщения – это механизм многократного использования алгоритма. Разработчик описывает алгоритм, но не указывает типы данных, с которыми тот работает. Применяя алгоритм, другой разработчик должен указать конкретные типы данных. Например, обобщённый список определяется как List<T>. Здесь переменная T, указывающая на тип называется параметром типа. В объявлении обобщённого типа её можно использовать в любом месте, где указывается, тип данных. Например:
- в параметре метода
public void Add(T item);
- в возвращаемом значенииpublic T[] ToArray();
public T this[int index] { get; set; }
- при определении локальных переменных и полей внутри классаMicrosoft рекомендует называть параметры типа
T
или словом, начинающимся с T
(TKey
, TResult
). При этом рекомендуется использовать значимые имена, где это возможно или если параметр типа ограничен конкретным классом или интерфейсом (например, параметр типа, ограниченный интерфейсом ISession
, может быть назван TSession
).Преимущества обобщений:
- защита исходного кода: для использования обобщённого алгоритма не нужен доступ к исходному тексту алгоритма;
- безопасность типов: компилятор и CLR обеспечивают, чтобы в алгоритме использовались лишь объекты, совместимые с указанным типом данных;
- более простой и понятный код: из-за того, что компилятор обеспечивает безопасность типов уже на этапе компиляции, требуется меньше операций приведения типов, а такой код проще писать и поддерживать;
- повышение производительности: не требуется лишних операций приведения типов, для структур не требуется упаковки/разупаковки, что ускоряет работу кода.
Наиболее частым видом использования обобщённых типов являются коллекции, такие как связанные списки, хэш-таблицы, стеки, очереди, деревья и т.п. Операции, вроде добавления и удаления элементов коллекции, выполняются примерно одинаково вне зависимости от типа хранимых данных. В большинстве случаев, когда требуется класс коллекции, рекомендуется использовать классы, определённые в библиотеке классов .NET.
При создании ваших собственных обобщённых типов, важно учитывать следующие моменты:
1. Какие типы обобщать в параметры типа. Обычно, чем больше типов вы параметризуете, тем более гибким становится ваш код. Но слишком обобщённый тип может привести к коду, который будет трудно читать и понять другим разработчикам.
2. Какие ограничения, если они нужны, применить к параметрам типа (об ограничениях в следующих постах). Хорошим тоном будет применить максимально возможные ограничения, которые всё равно позволят вам использовать требуемые типы. Например, если обобщённый тип подразумевает использование только ссылочных типов, добавьте ограничение
class
. Это не допустит использования вашего типа со структурами.3. Обдумайте обобщённое поведение в базовом классе и подклассах.
4. Нужно ли реализовать один или больше обобщённых интерфейсов. Например, если вы разрабатываете класс, который будет использоваться в качестве элемента обобщённой коллекции, вам может потребоваться реализовать интерфейс вроде
IComparable<T>
, где T
– это тип вашего класса.Продолжение следует…
Источники:
- Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 12.
- https://docs.microsoft.com/ru-ru/dotnet/csharp/programming-guide/generics/index
👍3
День двадцатый. #Оффтоп
Stackoverflow
Наверное, самый известный из используемых мной ресурсов. Захожу туда почти ежедневно и не по разу, чуть реже, чем в гугл. То есть, строго говоря, 9 из 10 вопросов к гуглу по программированию ведут на stackoverflow.com (английский естественно). Уже лет 15 как решил для себя, что поиск ответа с гораздо большей вероятностью приведёт к желаемому результату, если вопрос задан пусть даже на ломаном английском, нежели на русском. И тут нет ничего странного. Англоговорящее (и даже ломано-англоговорящее) сообщество гораздо обширнее русскоговорящего (хотя русскоязычная версия сайта тоже есть - ru.stackoverflow.com). Но все попытки поискать информацию по-русски меня почему-то зачастую вели на малознакомые и странноватые форумы, да и там часто только задавался мой вопрос, а ответа на него не было.
Стараюсь вспомнить случаи, когда решения какого-нибудь вопроса или разновидности решения не было на stackoverflow, и вспоминаю за все эти годы от силы случаев 10. При этом в основном это были узкоспециализированные вопросы по нюансам работы какой-нибудь специфической библиотеки или плагина, вроде jQuery Datatables.
Одно время я даже пытался заработать там немного рейтинга, отвечая на вопросы, но это невероятно сложно, потому что там настоящая гонка. Вопрос не успевает появиться, и уже через минуту-другую под ним 2-3 ответа или комментария. Так что в принципе, даже задав там вопрос, ответа долго ждать не пришлось бы. Но я так и не добрался пока до того, чтобы что-то спросить. Причина – см. предыдущий абзац :)
Stackoverflow
Наверное, самый известный из используемых мной ресурсов. Захожу туда почти ежедневно и не по разу, чуть реже, чем в гугл. То есть, строго говоря, 9 из 10 вопросов к гуглу по программированию ведут на stackoverflow.com (английский естественно). Уже лет 15 как решил для себя, что поиск ответа с гораздо большей вероятностью приведёт к желаемому результату, если вопрос задан пусть даже на ломаном английском, нежели на русском. И тут нет ничего странного. Англоговорящее (и даже ломано-англоговорящее) сообщество гораздо обширнее русскоговорящего (хотя русскоязычная версия сайта тоже есть - ru.stackoverflow.com). Но все попытки поискать информацию по-русски меня почему-то зачастую вели на малознакомые и странноватые форумы, да и там часто только задавался мой вопрос, а ответа на него не было.
Стараюсь вспомнить случаи, когда решения какого-нибудь вопроса или разновидности решения не было на stackoverflow, и вспоминаю за все эти годы от силы случаев 10. При этом в основном это были узкоспециализированные вопросы по нюансам работы какой-нибудь специфической библиотеки или плагина, вроде jQuery Datatables.
Одно время я даже пытался заработать там немного рейтинга, отвечая на вопросы, но это невероятно сложно, потому что там настоящая гонка. Вопрос не успевает появиться, и уже через минуту-другую под ним 2-3 ответа или комментария. Так что в принципе, даже задав там вопрос, ответа долго ждать не пришлось бы. Но я так и не добрался пока до того, чтобы что-то спросить. Причина – см. предыдущий абзац :)
День двадцать первый. #ЗаметкиНаПолях
Ограничения обобщений
Ограничения сообщают компилятору, какими характеристиками должен обладать параметр типа. Без ограничений параметр типа может быть любым. Если клиентский код попытается использовать ваш класс с типом, который не совместим с ограничением, возникнет ошибка компиляции. Синтаксис:
Неограниченные параметры типа
Параметры типа, не имеющие ограничений, называются неограниченными. К ним применяются следующие правила:
- операторы
- они могут быть приведены к типу
- вы можете сравнивать их с
Зачем использовать ограничения
Ограничивая параметр типа, вы увеличиваете количество доступных операций и вызовов методов теми, которые поддерживаются типом-ограничением и всеми типами в его иерархии наследования. Неограниченные параметры типа поддерживают только простейшие операции и методы, определённые в
При применении ограничения
Ограничения нескольких параметров
Вы можете применить ограничения к нескольким параметрам, и несколько ограничений к одному параметру. Cами ограничения могут быть обобщёнными типами:
Позволяет указать, что между указанными аргументами типа должны быть определённые отношения:
Источник: https://docs.microsoft.com/ru-ru/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters
Ограничения обобщений
Ограничения сообщают компилятору, какими характеристиками должен обладать параметр типа. Без ограничений параметр типа может быть любым. Если клиентский код попытается использовать ваш класс с типом, который не совместим с ограничением, возникнет ошибка компиляции. Синтаксис:
public class SomeList<T> where T : BaseClassТипы ограничений:
{
…
}
T : struct
– значимый тип (структура);T : class
– ссылочный тип: класс, интерфейс, делегат (C#7.3+) или массив;T : new()
– аргумент типа должен иметь конструктор без параметров; T : <имя класса>
- аргумент типа должен быть этого класса или наследником класса;T : <имя интерфейса>
- аргумент типа должен быть этим интерфейсом или классом, реализующим этот интерфейс (можно добавлять несколько ограничений интерфейса, тогда аргумент типа должен реализовывать все интерфейсы);T : TBase
– параметры типа T
и TBase
такие, что T
должен быть типом TBase
или наследовать от него;T : unmanaged
(C#7.3+) – не ссылочный тип и не имеет членов ссылочного типа на всех уровнях наследования (значимые примитивные типы, указатели, перечисления или структуры, определённые пользователем). Неограниченные параметры типа
Параметры типа, не имеющие ограничений, называются неограниченными. К ним применяются следующие правила:
- операторы
!=
и ==
не могут быть использованы, поскольку нет гарантии, что конкретные аргументы типа их поддерживают;- они могут быть приведены к типу
System.Object
или явно к любому интерфейсному типу;- вы можете сравнивать их с
null
(значимые типы при этом всегда возвратят false
).Зачем использовать ограничения
Ограничивая параметр типа, вы увеличиваете количество доступных операций и вызовов методов теми, которые поддерживаются типом-ограничением и всеми типами в его иерархии наследования. Неограниченные параметры типа поддерживают только простейшие операции и методы, определённые в
System.Object
.При применении ограничения
where T : class
избегайте использования операторов ==
и !=
к параметру типа, поскольку эти операторы проверяют только идентичность ссылки, а не значения. Для проверки по значению используйте ограничения where T : IEquatable<T>
или where T : IComparable<T>
и реализуйте эти интерфейсы в классах, которые будут использоваться в обобщённом классе. Ограничения нескольких параметров
Вы можете применить ограничения к нескольким параметрам, и несколько ограничений к одному параметру. Cами ограничения могут быть обобщёнными типами:
class Base { }Параметры типа как ограничения
class Test<T, U>
where U : struct
where T : Base, System.IComparable<T>, new()
{ }
Позволяет указать, что между указанными аргументами типа должны быть определённые отношения:
private static List<TBase> ConvertList<T, TBase>(IList<T> list) where T : TBaseЗдесь параметр
{
// преобразование списка T в список TBase
…
}
T
ограничен параметром TBase
. То есть аргумент, заданный для T
, должен быть совместим с аргументом, заданным для TBase
.Источник: https://docs.microsoft.com/ru-ru/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters
👍1
День двадцать второй. #CodeComplete
4. Утверждения
Утверждение – условие, которое всегда верно; предположение о дизайне, на котором основана процедура.
- документируйте предположения о дизайне через утверждения вместо комментариев;
- документируйте условия, которые всегда верны – в 100% случаев;
- если проверка утверждения терпит неудачу, правильное действие – изменить и перекомпилировать код;
- концептуально утверждение — это помощь при разработке, оно не используется в продакшн-коде (удаляется при компиляции).
Роль утверждений:
- полезны при использовании метода проектирования по контракту (с предусловиями и постусловиями);
- полезны в больших и сложных программах;
- используются как защитная техника, чтобы люди правильно использовали ваш код.
Важно:
Не путайте утверждения с обработкой ошибок!
4. Утверждения
Утверждение – условие, которое всегда верно; предположение о дизайне, на котором основана процедура.
- документируйте предположения о дизайне через утверждения вместо комментариев;
- документируйте условия, которые всегда верны – в 100% случаев;
- если проверка утверждения терпит неудачу, правильное действие – изменить и перекомпилировать код;
- концептуально утверждение — это помощь при разработке, оно не используется в продакшн-коде (удаляется при компиляции).
Роль утверждений:
- полезны при использовании метода проектирования по контракту (с предусловиями и постусловиями);
- полезны в больших и сложных программах;
- используются как защитная техника, чтобы люди правильно использовали ваш код.
Важно:
Не путайте утверждения с обработкой ошибок!
День двадцать третий. #ЗаметкиНаПолях
Ковариантность и Контравариантность в Обобщениях (начало)
Очень обширная тема, поэтому разобью её на четыре поста:
1. Общие понятия
2. Вариантность в обобщённых интерфейсах
3. Вариантность в обобщённых делегатах
4. Определение вариантных интерфейсов и делегатов
1. Общие понятия
Ковариантность и контравариантность относятся к возможности использования более конкретного (производного) типа или более общего (базового) типа, чем изначально обозначенный. Обобщённые параметры типа поддерживают ковариантность и конртавариантность, чтобы предоставлять большую гибкость в присвоении и использовании обобщённых типов. В следующих примерах допустим, что
Инвариантность означает, что вы можете использовать только изначально определённый тип.
Ковариантность позволяет вам использовать производный (более конкретный) тип, чем изначально обозначенный. Вы можете присвоить экземпляр
Ковариантные параметры типа позволяют вам делать присваивания, похожие на обычный полиморфизм:
Котравариантность позволяет вам использовать более общий (базовый) тип, чем изначально обозначенный. Вы можете присвоить экземпляр делегата
Контравариантность выглядит контринтуитивной. В следующем примере создаётся делегат типа
В общем случае ковариантный параметр типа может быть использован в качестве типа возвращаемого значения делегата, а контравариантный – как тип параметра. В случае интерфейсов ковариантные параметры типа могут быть использованы как типы возвращаемых значений методов интерфейса, а контравариантные – как типы параметров методов интерфейса.
Продолжение следует…
Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
Ковариантность и Контравариантность в Обобщениях (начало)
Очень обширная тема, поэтому разобью её на четыре поста:
1. Общие понятия
2. Вариантность в обобщённых интерфейсах
3. Вариантность в обобщённых делегатах
4. Определение вариантных интерфейсов и делегатов
1. Общие понятия
Ковариантность и контравариантность относятся к возможности использования более конкретного (производного) типа или более общего (базового) типа, чем изначально обозначенный. Обобщённые параметры типа поддерживают ковариантность и конртавариантность, чтобы предоставлять большую гибкость в присвоении и использовании обобщённых типов. В следующих примерах допустим, что
Animal
– базовый класс, а Cat
– производный от него.Инвариантность означает, что вы можете использовать только изначально определённый тип.
Ковариантность позволяет вам использовать производный (более конкретный) тип, чем изначально обозначенный. Вы можете присвоить экземпляр
IEnumerable<Cat>
переменной типа IEnumerable<Animal>
.Ковариантные параметры типа позволяют вам делать присваивания, похожие на обычный полиморфизм:
IEnumerable<Cat> cats = new List<Cat>();Класс
IEnumerable<Animal> animals = cats;
List<T>
реализует интерфейс IEnumerable<T>
, поэтому List<Cat>
реализует IEnumerable<Cat>
. А ковариантный параметр типа делает всё остальное.Котравариантность позволяет вам использовать более общий (базовый) тип, чем изначально обозначенный. Вы можете присвоить экземпляр делегата
Action<Animal>
переменной типа Action<Cat>
.Контравариантность выглядит контринтуитивной. В следующем примере создаётся делегат типа
Action<Animal>
, а затем он присваивается переменной типа Action<Cat>
.Action<Animal> actAnimal = (target)=>{Console.WriteLine(target.GetType().Name);};Лямбда-выражение создаёт метод-делегат, который принимает один параметр типа
Action<Cat> actCat = actAnimal;
actCat(new Cat()); // выводит “Cat”
Animal
, и у которого нет возвращаемого значения. Этот делегат может быть присвоен переменной типа Action<Cat>
, поскольку параметр T
делегата Action<T>
контравариантен. Когда делегат типа Action<Animal>
вызывается так, как если бы он был типа Action<Cat>
, его аргумент должен быть типа Cat
. В общем случае ковариантный параметр типа может быть использован в качестве типа возвращаемого значения делегата, а контравариантный – как тип параметра. В случае интерфейсов ковариантные параметры типа могут быть использованы как типы возвращаемых значений методов интерфейса, а контравариантные – как типы параметров методов интерфейса.
Продолжение следует…
Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
👍1
День двадцать четвёртый. #ЗаметкиНаПолях
Ковариантность и Контравариантность в Обобщениях (продолжение)
2. Вариантность в обобщённых интерфейсах
Обобщённые интерфейсы с ковариантными параметрами типа
В .NET Framework 4+ несколько обобщённых интерфейсов имеют ковариантные параметры типа, например:
Обобщённые интерфейсы с контравариантными обобщёнными параметрами типа
В .NET Framework 4+ несколько обобщённых интерфейсов имеют контравариантные параметры типа, например:
Следующий пример иллюстрирует контравариантные параметры типа. В примере объявляется абстрактный класс
Класс
Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
Ковариантность и Контравариантность в Обобщениях (продолжение)
2. Вариантность в обобщённых интерфейсах
Обобщённые интерфейсы с ковариантными параметрами типа
В .NET Framework 4+ несколько обобщённых интерфейсов имеют ковариантные параметры типа, например:
IEnumerable<T>
, IEnumerator<T>
, IQueryable<T>
, или IGrouping<TKey,TElement>
. Все параметры типа этих интерфейсов ковариантны, поэтому они используются только для типов возвращаемых значений методов. Например, для вышеупомянутых классов Animal
и производного от него Cat
:public void Feed(IEnumerable<Animal> animals) {…}Поскольку параметр типа интерфейса
…
List<Cat> catList = new List<Cat>();
Feed(catList);
IEnumerable<Animal> animalIEnum = catList;
IEnumerable
ковариантен, а List<T>
реализует IEnumerable<T>
, то можно вызывать метод, принимающий IEnumerable<Animal>
с параметром List<Cat>
, а также присваивать переменной типа IEnumerable<Animal>
экземпляр типа List<Cat>
без приведения типов.Обобщённые интерфейсы с контравариантными обобщёнными параметрами типа
В .NET Framework 4+ несколько обобщённых интерфейсов имеют контравариантные параметры типа, например:
IComparer<T>
, IComparable<T>
, или IEqualityComparer<T>
. Эти интерфейсы имеют только контравариантные параметры типа, поэтому параметры типа используются только в качестве параметров методов интерфейсов. Следующий пример иллюстрирует контравариантные параметры типа. В примере объявляется абстрактный класс
Shape
со свойством Area
и классShapeAreaComparer
, реализующий IComparer<Shape>
. Реализация метода Compare
этого интерфейса основана на сравнении площадей фигур по свойству Area
, поэтому ShapeAreaComparer
может быть использован для сортировки объектов Shape
по площади.Класс
Circle
наследует от Shape
и переопределяет Area
. Пример создаёт сортированную коллекцию SortedSet<T>
из объектов Circle
, используя конструктор, принимающий параметр IComparer<Circle>
. Но вместо передачи параметра типа IComparer<Circle>
, пример передаёт объект ShapeAreaComparer
, реализующий IComparer<Shape>
. Пример может передавать параметр базового (более общего) типа, поскольку параметр типа обобщённого интерфейса IComparer<T>
контравариантен. Контравариантность позволяет ShapeAreaComparer
сортировать коллекцию любого типа или коллекцию объектов разных типов, которые наследуют от Shape
.using System;Этот код производит следующий вывод:
using System.Collections.Generic;
abstract class Shape
{
public virtual double Area { get { return 0; }}
}
class Circle : Shape
{
private double r;
public Circle(double radius) { r = radius; }
public double Radius { get { return r; }}
public override double Area
{
get { return Math.PI * r * r; }
}
}
class ShapeAreaComparer : IComparer<Shape>
{
int IComparer<Shape>.Compare(Shape a, Shape b)
{
if (a == null) return b == null ? 0 : -1;
return b == null ? 1 : a.Area.CompareTo(b.Area);
}
}
class Program
{
static void Main()
{
SortedSet<Circle> circlesByArea =
new SortedSet<Circle>(new ShapeAreaComparer())
{
new Circle(7.2),
new Circle(100),
null,
new Circle(.01)
};
foreach (Circle c in circlesByArea)
{
Console.WriteLine(c == null ?
"null" :
"Круг площадью " + c.Area);
}
}
}
nullАналогично можно сделать, например, объект
Круг площадью 0.000314159265358979
Круг площадью 162.860163162095
Круг площадью 31415.9265358979
Square
, производный от Shape
, и создать коллекцию объектов Square
, используя тот же класс ShapeAreaComparer
:SortedSet<Square> squaresByArea = new SortedSet<Square>(new ShapeAreaComparer());Продолжение следует…
Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
👍2
День двадцать пятый. #ЗаметкиНаПолях
Ковариантность и Контравариантность в Обобщениях (продолжение)
3. Вариантность в обобщённых делегатах
В .NET Framework 4+ обобщённые делегаты
Примечание. Последний параметр типа обобщённых делегатов Func указывает тип возвращаемого значения в сигнатуре делегата. Он является ковариантным, в то время как остальные параметры типа являются контравариантными.
Пусть объявлена следующая переменная:
- тип параметра ковариантен, а
- тип возвращаемого значения контравариантен, а
Пример показывает, что этот обобщённый делегат может храниться в переменных или параметрах метода, имеющих более конкретные типы параметров и более общие возвращаемые типы, при условии, что все типы делегата сконструированы из обобщённого типа делегата
Окончание следует…
Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
Ковариантность и Контравариантность в Обобщениях (продолжение)
3. Вариантность в обобщённых делегатах
В .NET Framework 4+ обобщённые делегаты
Func
, такие как Func<T,TResult>
, имеют ковариантные возвращаемые типы и контравариантные типы параметров. Обобщённые делегаты Action
, такие как Action<T1,T2>
, имеют контравариантные типы параметров. Это означает, что делегаты можно присваивать переменным, имеющим производные (более конкретные) типы параметров и (в случае обобщённых делегатов Func
) базовые (более общие) возвращаемые типы.Примечание. Последний параметр типа обобщённых делегатов Func указывает тип возвращаемого значения в сигнатуре делегата. Он является ковариантным, в то время как остальные параметры типа являются контравариантными.
Пусть объявлена следующая переменная:
Func<object, ArgumentException> fn1 = null;Тогда можно привести её к типу
Func
с отличающимися параметрами обобщённого типа:Func<string, Exception> fn2 = fn1; //явного приведения типа не требуетсяТакое присвоение можно сделать без приведения типа, поскольку:
Ecxeption e = fn2(“ ”);
- тип параметра ковариантен, а
string
наследует от object
,- тип возвращаемого значения контравариантен, а
Exception
– базовый класс для ArgumentException.Пример показывает, что этот обобщённый делегат может храниться в переменных или параметрах метода, имеющих более конкретные типы параметров и более общие возвращаемые типы, при условии, что все типы делегата сконструированы из обобщённого типа делегата
Func<T,TResult>
.Окончание следует…
Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
День двадцать шестой. #ЗаметкиНаПолях
Ковариантность и Контравариантность в Обобщениях (окончание)
4. Определение вариантных интерфейсов и делегатов
В .NET Framework 4+ в C# появились ключевые слова, позволяющие помечать параметры обобщённого типа интерфейсов и делегатов как ковариантные или контравариантные.
Параметр ковариантного типа помечается ключевым словом
Примечание. Если метод интерфейса имеет параметр типа обобщённого делегата, параметр ковариантного типа этого интерфейса может использоваться для указания параметра контравариантного типа этого типа делегата.
Параметр контравариантного типа помечается ключевым словом
Параметры вариантного типа могут иметь только интерфейсы и делегаты. Тип интерфейса или тип делегата может иметь как ковариантные, так и контравариантные параметры типа.
Следующий пример показывает определение ковариантного и контравариантного интерфейсов. Пусть есть абстрактные классы животное (
Ковариантность и Контравариантность в Обобщениях (окончание)
4. Определение вариантных интерфейсов и делегатов
В .NET Framework 4+ в C# появились ключевые слова, позволяющие помечать параметры обобщённого типа интерфейсов и делегатов как ковариантные или контравариантные.
Параметр ковариантного типа помечается ключевым словом
out
. Параметр ковариантного типа можно использовать как возвращаемое значение метода, принадлежащего интерфейсу, или как возвращаемый тип делегата. Параметр ковариантного типа нельзя использовать как ограничение обобщённого типа для методов интерфейса.Примечание. Если метод интерфейса имеет параметр типа обобщённого делегата, параметр ковариантного типа этого интерфейса может использоваться для указания параметра контравариантного типа этого типа делегата.
Параметр контравариантного типа помечается ключевым словом
in
. Параметр контравариантного типа можно использовать как тип параметра метода, принадлежащего интерфейсу, или как тип параметра делегата. Параметр контравариантного типа можно использовать как ограничение обобщённого типа для метода интерфейса.Параметры вариантного типа могут иметь только интерфейсы и делегаты. Тип интерфейса или тип делегата может иметь как ковариантные, так и контравариантные параметры типа.
Следующий пример показывает определение ковариантного и контравариантного интерфейсов. Пусть есть абстрактные классы животное (
Animal
) и млекопитающее (Mammal
). И производные от класса млекопитающего классы лошадь (Horse
) и кот (Cat
). В ковариантном интерфейсе фермы (IFarm
), определён метод GetNextAnimal
, возвращающий следующее животное. А в контравариантном интерфейсе кормилки для животных (IFeeder
), определён обобщённый метод Feed
имеющий тип параметра U
, ограниченный типом параметра интерфейса T
:abstract class Animal {}Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
abstract class Mammal : Animal {}
class Horse : Mammal {}
class Cat : Mammal {}
interface IFarm<out T> where T : Animal
{
T GetNextAnimal();
}
interface IFeeder<in T> where T : Animal
{
void Feed<U>(U animal) where U : T;
}
class Program
{
static void Main(string[] args)
{
IFarm<Horse> horsefarm = null;
IFarm<Animal> animalfarm = horsefarm;
Animal animal = horsefarm.GetNextAnimal();
IFeeder<Animal> feeder = null;
feeder.Feed<Mammal>(new Horse());
feeder.Feed<Mammal>(new Cat());
}
}
День двадцать седьмой. #CodeComplete
5. Отладка
Иногда бывает полезным проходить новый код по шагам в отладчике, наблюдая за изменением значений переменных. Это может быть формой самостоятельного обзора кода. Кроме того, это помогает уменьшить временной интервал между внесением ошибки и обнаружением её.
Этапы отладки:
1) Стабилизация ошибки. Найдите сценарий, при котором ошибка всегда проявляется.
2) Локализация источника ошибки. Точно найдите место в коде, где возникает ошибка.
3) Исправление ошибки.
4) Проверка и тестирование исправления.
5) Поиск похожих ошибок. Определите тип ошибки и проверьте, нет ли в остальном коде ошибок этого типа.
Советы по обнаружению ошибок
Следующие советы не обязательно применимы ко всем случаям, но могут помочь в некоторых сложных ситуациях:
- используйте все доступные данные для формулирования гипотезы о сути ошибки;
- создайте точные тесты, которые приводят к ошибке;
- воссоздайте ошибку несколькими разными способами;
- по ситуации: сузьте/расширьте сектор кода для поиска ошибки;
- проверьте методы, которые имели ошибки ранее (часто исправление одной ошибки приводит к возникновению другой);
- проверьте недавно изменённый код;
- проверьте код на типичные ошибки (широко распространённые вообще, вроде деления на ноль, нулевых указателей, выхода за границы массива и т.п., либо типичные для вашей области/организации/вас лично);
- поговорите с другим человеком (обзор кода): часто просто объяснение проблемы кому-то другому помогает понять, что не так;
- сделайте паузу: займитесь чем-то отвлечённым на некоторое время;
- прочитайте все предупреждения компилятора: предупреждения компилятора не сообщают об ошибках напрямую, но могут свидетельствовать о недостатках в коде, приводящих к ошибкам в связанных с этим блоком местах;
- проверьте, не является ли это ошибкой проектирования: плохо спроектированный класс или метод иногда проще переписать заново, чем постоянно исправлять;
- проверьте, нет ли ошибки в тестовых данных: написанию тестов обычно уделяют меньше внимания, чем основному коду, поэтому ошибки в тестах встречаются гораздо чаще;
- изначально избегайте ошибок: правильное проектирование и написание кода приводит к значительному сокращению количества ошибок.
Инструкция по исправлению ошибки:
1) убедитесь, что вы понимаете проблему, прежде чем исправлять её;
2) убедитесь, что вы понимаете всю программу, а не только проблемную часть;
3) подтвердите диагноз: убедитесь с помощью различных тестов, что ошибка именно здесь и именно в этом;
4) расслабьтесь :)
5) сохраните исходный вариант кода (системы управления версиями очень помогают);
6) делайте ОДНО исправление за раз: не пытайтесь исправить сразу несколько ошибок одновременно, это приводит к возникновению новых ошибок;
7) проверьте ваше исправление;
8) выполните юнит-тесты (не только относящиеся к исправлению, чтобы проверить, не привело ли исправление к ошибкам в других местах);
9) выполните интеграционные тесты с изменённым кодом;
10) поищите похожие ошибки.
Отладка с применением грубой силы
1) Установите максимальное время на отладку обычным способом, а потом рассмотрите вариант перехода на отладку грубой силой.
2) Составьте список вещей, которые стоит проверить.
Примеры отладки грубой силой:
- переписывание кода с нуля;
- компиляция кода с полной отладочной информацией;
- установка прерывания на каждом исключении;
- создание набора автоматизированных тестов и запуск их на всю ночь;
- выполнение полного обзора проекта/кода;
- точное воссоздание конфигурации оборудования конечного пользователя.
5. Отладка
Иногда бывает полезным проходить новый код по шагам в отладчике, наблюдая за изменением значений переменных. Это может быть формой самостоятельного обзора кода. Кроме того, это помогает уменьшить временной интервал между внесением ошибки и обнаружением её.
Этапы отладки:
1) Стабилизация ошибки. Найдите сценарий, при котором ошибка всегда проявляется.
2) Локализация источника ошибки. Точно найдите место в коде, где возникает ошибка.
3) Исправление ошибки.
4) Проверка и тестирование исправления.
5) Поиск похожих ошибок. Определите тип ошибки и проверьте, нет ли в остальном коде ошибок этого типа.
Советы по обнаружению ошибок
Следующие советы не обязательно применимы ко всем случаям, но могут помочь в некоторых сложных ситуациях:
- используйте все доступные данные для формулирования гипотезы о сути ошибки;
- создайте точные тесты, которые приводят к ошибке;
- воссоздайте ошибку несколькими разными способами;
- по ситуации: сузьте/расширьте сектор кода для поиска ошибки;
- проверьте методы, которые имели ошибки ранее (часто исправление одной ошибки приводит к возникновению другой);
- проверьте недавно изменённый код;
- проверьте код на типичные ошибки (широко распространённые вообще, вроде деления на ноль, нулевых указателей, выхода за границы массива и т.п., либо типичные для вашей области/организации/вас лично);
- поговорите с другим человеком (обзор кода): часто просто объяснение проблемы кому-то другому помогает понять, что не так;
- сделайте паузу: займитесь чем-то отвлечённым на некоторое время;
- прочитайте все предупреждения компилятора: предупреждения компилятора не сообщают об ошибках напрямую, но могут свидетельствовать о недостатках в коде, приводящих к ошибкам в связанных с этим блоком местах;
- проверьте, не является ли это ошибкой проектирования: плохо спроектированный класс или метод иногда проще переписать заново, чем постоянно исправлять;
- проверьте, нет ли ошибки в тестовых данных: написанию тестов обычно уделяют меньше внимания, чем основному коду, поэтому ошибки в тестах встречаются гораздо чаще;
- изначально избегайте ошибок: правильное проектирование и написание кода приводит к значительному сокращению количества ошибок.
Инструкция по исправлению ошибки:
1) убедитесь, что вы понимаете проблему, прежде чем исправлять её;
2) убедитесь, что вы понимаете всю программу, а не только проблемную часть;
3) подтвердите диагноз: убедитесь с помощью различных тестов, что ошибка именно здесь и именно в этом;
4) расслабьтесь :)
5) сохраните исходный вариант кода (системы управления версиями очень помогают);
6) делайте ОДНО исправление за раз: не пытайтесь исправить сразу несколько ошибок одновременно, это приводит к возникновению новых ошибок;
7) проверьте ваше исправление;
8) выполните юнит-тесты (не только относящиеся к исправлению, чтобы проверить, не привело ли исправление к ошибкам в других местах);
9) выполните интеграционные тесты с изменённым кодом;
10) поищите похожие ошибки.
Отладка с применением грубой силы
1) Установите максимальное время на отладку обычным способом, а потом рассмотрите вариант перехода на отладку грубой силой.
2) Составьте список вещей, которые стоит проверить.
Примеры отладки грубой силой:
- переписывание кода с нуля;
- компиляция кода с полной отладочной информацией;
- установка прерывания на каждом исключении;
- создание набора автоматизированных тестов и запуск их на всю ночь;
- выполнение полного обзора проекта/кода;
- точное воссоздание конфигурации оборудования конечного пользователя.
👍1
День двадцать восьмой. #ЗаметкиНаПолях
Явная реализация интерфейсного метода (EIMI)
Явная реализация методов интерфейса служит двум главным целям:
1) Поскольку она недоступна через экземпляры класса, это позволяет исключить эти методы из общедоступного интерфейса класса. Это, в частности, полезно, когда класс реализует внутренний интерфейс, который не представляет интереса для пользователей класса.
Например,
Допустим, вы создали экземпляр
2) Явные реализации методов интерфейса позволяют избежать путаницы между двумя реализациями методов с одинаковой сигнатурой. Без этого классам было бы невозможно иметь разные реализации интерфейсных членов с одинаковой сигнатурой и типом возвращаемого значения, а также классам было бы невозможно иметь вообще какие-либо реализации членов интерфейса с одинаковой сигнатурой, но разными типами возвращаемого значения.
В следующем примере класс ресторана реализует два интерфейса для выдачи вариантов меню: на вынос
Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 13.
Явная реализация интерфейсного метода (EIMI)
class SomeType : IDisposableЕсли в C# перед именем метода вы ставите имя интерфейса, в котором определён этот метод, то вы создаёте явную реализацию интерфейсного метода. Этому методу нельзя указывать область доступа (
{
public void Dispose()
{
Console.WriteLine(“public Dispose”);
}
void IDisposable.Dispose()
{
Console.WriteLine(“IDisposable.Dispose”);
}
}
private
или public
), однако компилятор, создавая метаданные для метода, делает его private
. Поэтому единственный способ вызвать интерфейсный метод – обратиться через переменную этого интерфейсного типа.Явная реализация методов интерфейса служит двум главным целям:
1) Поскольку она недоступна через экземпляры класса, это позволяет исключить эти методы из общедоступного интерфейса класса. Это, в частности, полезно, когда класс реализует внутренний интерфейс, который не представляет интереса для пользователей класса.
Например,
List<T>
реализует IList<T>
неявно, а IList
(необобщённый интерфейс) явно. Это значит, что, когда вы используете класс напрямую, вы увидите только обобщённые методы и не увидите методы, принимающие Object
.Допустим, вы создали экземпляр
List<Person>
. Если бы IList
был реализован неявно, тогда у вас было бы два метода Add
, напрямую доступных из класса: Add(Person item)
и Add(Object item)
, что разрушило бы безопасность типов, предлагаемую обобщениями. Вызов list.Add("Foo")
успешно бы скомпилировался, поскольку была бы автоматически выбрана перегруженная версия Add(Object)
.2) Явные реализации методов интерфейса позволяют избежать путаницы между двумя реализациями методов с одинаковой сигнатурой. Без этого классам было бы невозможно иметь разные реализации интерфейсных членов с одинаковой сигнатурой и типом возвращаемого значения, а также классам было бы невозможно иметь вообще какие-либо реализации членов интерфейса с одинаковой сигнатурой, но разными типами возвращаемого значения.
В следующем примере класс ресторана реализует два интерфейса для выдачи вариантов меню: на вынос
IWindow
и «в ресторане» IRestaurant
. Эти интерфейсы имеют одинаковые методы GetMenu
, но с разными типами возвращаемого значения. Также ресторан может иметь свою собственную реализацию метода GetMenu
, не имеющую отношения к интерфейсным методам. Основная программа запрашивает все три метода, приводя экземпляр класса к интерфейсному типу для вызова явных реализаций интерфейсных методов.using System;
public abstract class Menu { }
public class WindowMenu : Menu { }
public class RestaurantMenu : Menu { }
public interface IWindow { WindowMenu GetMenu(); }
public interface IRestaurant { RestaurantMenu GetMenu(); }
public sealed class Pizzeria : IWindow, IRestaurant
{
//Явная реализация интерфейсного метода для IWindow
WindowMenu IWindow.GetMenu() => new WindowMenu();
//Явная реализация интерфейсного метода для IRestaurant
RestaurantMenu IRestaurant.GetMenu() => new RestaurantMenu();
//Дополнительный открытый метод, не имеющий отношения к интерфейсам
public string GetMenu() => "Public method";
}
class Program
{
static void Main(string[] args)
{
Pizzeria mp = new Pizzeria();
//открытый метод
Console.WriteLine(mp.GetMenu());
//Метод IWindow.GetMenu
var windowMenu = ((IWindow) mp).GetMenu();
Console.WriteLine(windowMenu.ToString());
//Метод IRestaurant.GetMenu
var restaurantMenu = ((IRestaurant) mp).GetMenu();
Console.WriteLine(restaurantMenu.ToString());
Console.ReadLine();
}
}
Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 13.
👍2
День двадцать девятый. #Оффтоп #МоиИнструменты
В качестве отвлечённой темы коротко опишу вам инструменты, которыми пользуюсь на регулярной основе. Оставим в покое редакторы кода, IDE и прочие программы для администрирования баз данных, поскольку они у каждого свои. А вот остальные помощники могут быть общими у разработчиков в совершенно разных сферах.
SVN и WinMerge
Начнём с контроля версий. У нас в компании используется SVN. Я к нему более-менее привык, поэтому он мне лично нравится больше гораздо более популярного Git. Хотя суть у них одна. Начинали мы больше 10 лет назад без контроля версий, и это был сущий кошмар. Мы реально отправляли файлы с изменениями менеджеру по почте. Теперь даже представить сложно, как люди обходились без контроля версий. В общем виде процесс следующий:
Первая ветка – песочница. Там все разработки и фиксы (исключительно рабочие, никаких недоделок, за редким исключением).
Вторая ветка – staging. Код для тестов на отдельном сервере менеджерами компании перед релизом.
Ну и, собственно, продакшн. Релизятся либо каждый баг/фича по одиночке через SVN Merge, либо собираются сразу несколько в недельный релиз. Тогда обычно мы делаем копию продакшн-ветки, сливаем туда всё, что хотим релизнуть, и тестируем эту ветку на staging-сервере. Потом продакшн-ветка переименовывается в архивную, а эта тестовая копия переименовывается в продакшн. Легко и просто.
WinMerge – полезная утилитка для слияния изменений. С её помощью можно посмотреть отличия между файлами и папками. Программа в большинстве случаев достаточно чётко выделяет строки с различиями, которые можно перенести из одного файла в другой (см. картинку ниже). Она хорошо дополняет SVN, помогая коммитить в SVN только те изменения в файле, которые надо, потому что SVN коммитит либо весь файл, либо ничего. Работает это следующим образом. У меня есть папка с кодом песочницы (в которой выполняются коммиты в SVN) и отдельная рабочая папка, не связанная с SVN. Когда надо что-то залить в SVN я сравниваю эти папки в WinMerge и переношу нужные изменения из рабочей папки в папку песочницы. Таким образом, если в одном файле есть изменения для разных багов, то с помощью WinMerge можно одни изменения перенести, а другие оставить для доработки.
При сравнении папок можно использовать различные фильтры для папок и файлов, например, исключить из сравнения папки .svn или сравнивать только файлы с кодом, пропуская картинки.
В качестве отвлечённой темы коротко опишу вам инструменты, которыми пользуюсь на регулярной основе. Оставим в покое редакторы кода, IDE и прочие программы для администрирования баз данных, поскольку они у каждого свои. А вот остальные помощники могут быть общими у разработчиков в совершенно разных сферах.
SVN и WinMerge
Начнём с контроля версий. У нас в компании используется SVN. Я к нему более-менее привык, поэтому он мне лично нравится больше гораздо более популярного Git. Хотя суть у них одна. Начинали мы больше 10 лет назад без контроля версий, и это был сущий кошмар. Мы реально отправляли файлы с изменениями менеджеру по почте. Теперь даже представить сложно, как люди обходились без контроля версий. В общем виде процесс следующий:
Первая ветка – песочница. Там все разработки и фиксы (исключительно рабочие, никаких недоделок, за редким исключением).
Вторая ветка – staging. Код для тестов на отдельном сервере менеджерами компании перед релизом.
Ну и, собственно, продакшн. Релизятся либо каждый баг/фича по одиночке через SVN Merge, либо собираются сразу несколько в недельный релиз. Тогда обычно мы делаем копию продакшн-ветки, сливаем туда всё, что хотим релизнуть, и тестируем эту ветку на staging-сервере. Потом продакшн-ветка переименовывается в архивную, а эта тестовая копия переименовывается в продакшн. Легко и просто.
WinMerge – полезная утилитка для слияния изменений. С её помощью можно посмотреть отличия между файлами и папками. Программа в большинстве случаев достаточно чётко выделяет строки с различиями, которые можно перенести из одного файла в другой (см. картинку ниже). Она хорошо дополняет SVN, помогая коммитить в SVN только те изменения в файле, которые надо, потому что SVN коммитит либо весь файл, либо ничего. Работает это следующим образом. У меня есть папка с кодом песочницы (в которой выполняются коммиты в SVN) и отдельная рабочая папка, не связанная с SVN. Когда надо что-то залить в SVN я сравниваю эти папки в WinMerge и переношу нужные изменения из рабочей папки в папку песочницы. Таким образом, если в одном файле есть изменения для разных багов, то с помощью WinMerge можно одни изменения перенести, а другие оставить для доработки.
При сравнении папок можно использовать различные фильтры для папок и файлов, например, исключить из сравнения папки .svn или сравнивать только файлы с кодом, пропуская картинки.
День тридцатый.
Сертификат Microsoft. Шаг 1
Итак, спустя месяц после запуска канала, думаю, пришло время хотя бы ознакомиться с тем, что предстоит сделать для получения сертификата по C#. Впоследствии есть планы получить сертификат и по MVC, но начнём с малого :)
Итак, вот собственно страница экзамена https://www.microsoft.com/ru-ru/learning/exam-70-483.aspx Здесь можно посмотреть требования к сдающим, что будет проверяться на тесте, посмотреть варианты для подготовки и даже запланировать сам экзамен. Для России экзамен стоит $80, поэтому к подготовке стоит отнестись серьёзно, просто так попробовать сдать сильно дорого выходит. Кстати, можно попробовать пройти пробный тест вот здесь https://global4.mindhub.com/70-483-programming-in-c-microsoft-official/p/MU-70-483?utm_source=microsoft&utm_medium=certpage&utm_campaign=msofficialpractice, но он тоже платный: доступ $99 на месяц, $109 – на два. Немного странная ценовая политика, как мне кажется. Хотя в штатах сам экзамен стоит $165, в этом случае может и имеет смысл потренироваться)))
Ладно, пока остановимся на требованиях. Кандидаты также должны иметь глубокое понимание следующих тем:
- Управление потоком и событиями программы
- Асинхронное программирование и потоки
- Проверка данных и работа с коллекциями данных, включая LINQ
- Обработка ошибок и исключений
- Работа с массивами и коллекциями
- Работа с переменными, операторами и выражениями
- Работа с классами и методами
- Операторы принятия решения и итеративные операторы
Начнём подготовку…
Сертификат Microsoft. Шаг 1
Итак, спустя месяц после запуска канала, думаю, пришло время хотя бы ознакомиться с тем, что предстоит сделать для получения сертификата по C#. Впоследствии есть планы получить сертификат и по MVC, но начнём с малого :)
Итак, вот собственно страница экзамена https://www.microsoft.com/ru-ru/learning/exam-70-483.aspx Здесь можно посмотреть требования к сдающим, что будет проверяться на тесте, посмотреть варианты для подготовки и даже запланировать сам экзамен. Для России экзамен стоит $80, поэтому к подготовке стоит отнестись серьёзно, просто так попробовать сдать сильно дорого выходит. Кстати, можно попробовать пройти пробный тест вот здесь https://global4.mindhub.com/70-483-programming-in-c-microsoft-official/p/MU-70-483?utm_source=microsoft&utm_medium=certpage&utm_campaign=msofficialpractice, но он тоже платный: доступ $99 на месяц, $109 – на два. Немного странная ценовая политика, как мне кажется. Хотя в штатах сам экзамен стоит $165, в этом случае может и имеет смысл потренироваться)))
Ладно, пока остановимся на требованиях. Кандидаты также должны иметь глубокое понимание следующих тем:
- Управление потоком и событиями программы
- Асинхронное программирование и потоки
- Проверка данных и работа с коллекциями данных, включая LINQ
- Обработка ошибок и исключений
- Работа с массивами и коллекциями
- Работа с переменными, операторами и выражениями
- Работа с классами и методами
- Операторы принятия решения и итеративные операторы
Начнём подготовку…
Docs
Экзамен 70-483: программирование на C# - Learn
👍1
День тридцать первый. #CodeComplete
6. Оптимизация кода
Оптимизация кода – это изменение программы на уровне детального кодирования с сохранением семантики кода с целью улучшения производительности.
К оптимизации кода не относятся:
- выбор алгоритма;
- изначальная разработка, нацеленная на высокую производительность.
Правила оптимизации:
1. НЕ ДЕЛАЙТЕ ЭТОГО!
2. (только для экспертов) Не делайте этого… пока. До тех пор, пока у вас нет идеального, но неоптимизированного решения.
Защитный подход к оптимизации кода:
1. Пишите изначальный код, делая акцент на простоту, читаемость, удобство сопровождения, модульность и т.п.
!!! Только если производительность неудовлетворительная !!!
2. Измерьте слабые места производительности.
3. Внедрите изменения с сохранением семантики ПО ОДНОМУ ЗА РАЗ.
4. Измерьте каждое изменение; отмените 2/3 изменений, которые не улучшили производительность.
5. Повторяйте, пока не будет достигнут желаемый результат.
ВАЖНО:
1. Нужно измерить, чтобы быть уверенным.
2. Если вы не измерили, вы НЕ МОЖЕТЕ БЫТЬ УВЕРНЫ!
Общие принципы:
1. Результаты зачастую контринтуитивны. Попытки оптимизировать код, основываясь на теоретических предположениях о скорости работы того или иного кода, часто приводят к замедлению программы.
2. Результаты различаются в зависимости от языков, компиляторов, версий компиляторов.
Не думайте, что вы умнее компилятора и его создателей. В подавляющем большинстве случаев компилятор создаёт более эффективный машинный код из стандартных блоков кода, чем из «хитро оптимизированного» вами кода.
6. Оптимизация кода
Оптимизация кода – это изменение программы на уровне детального кодирования с сохранением семантики кода с целью улучшения производительности.
К оптимизации кода не относятся:
- выбор алгоритма;
- изначальная разработка, нацеленная на высокую производительность.
Правила оптимизации:
1. НЕ ДЕЛАЙТЕ ЭТОГО!
2. (только для экспертов) Не делайте этого… пока. До тех пор, пока у вас нет идеального, но неоптимизированного решения.
Защитный подход к оптимизации кода:
1. Пишите изначальный код, делая акцент на простоту, читаемость, удобство сопровождения, модульность и т.п.
!!! Только если производительность неудовлетворительная !!!
2. Измерьте слабые места производительности.
3. Внедрите изменения с сохранением семантики ПО ОДНОМУ ЗА РАЗ.
4. Измерьте каждое изменение; отмените 2/3 изменений, которые не улучшили производительность.
5. Повторяйте, пока не будет достигнут желаемый результат.
ВАЖНО:
1. Нужно измерить, чтобы быть уверенным.
2. Если вы не измерили, вы НЕ МОЖЕТЕ БЫТЬ УВЕРНЫ!
Общие принципы:
1. Результаты зачастую контринтуитивны. Попытки оптимизировать код, основываясь на теоретических предположениях о скорости работы того или иного кода, часто приводят к замедлению программы.
2. Результаты различаются в зависимости от языков, компиляторов, версий компиляторов.
Не думайте, что вы умнее компилятора и его создателей. В подавляющем большинстве случаев компилятор создаёт более эффективный машинный код из стандартных блоков кода, чем из «хитро оптимизированного» вами кода.
День тридцать второй. #Оффтоп #МоиИнструменты
Продолжу серию об инструментах, которыми я пользуюсь ежедневно. На очереди Jing от TechSmith. Да, штука старая (выпускалась с 2007 по 2012 годы), но тем не менее своё нехитрое дело выполняет.
Очень удобный инструмент для захвата области экрана как в картинку, так и в видео. Пользуемся с коллегами постоянно, когда надо объяснить друг другу, где косяк, что надо исправить или как протестировать исправление (куда нажать и в какой последовательности). Программа позволяет захватить любую область экрана, нажатием сочетания клавиш по вашему выбору. Кстати, прога умеет определять границы окон и даже документов внутри окон, поэтому часто не нужно вымерять размеры до пикселя, она сама предложит заскринить нужную область. Затем в случае с картинкой можно добавить туда выделение, рамки, стрелки и текст (см. картинку ниже). Вполне достаточно, чтобы графически выразить свою мысль, а не писать целый абзац текста вроде «в выпадающем списке в верхнем левом углу выберите пункт XXX, отметьте флажок YYY и слева внизу страницы нажмите кнопку ZZZ».
Потом картинку можно как просто скопировать в буфер, так и сохранить локально или разместить на условно бесплатном хостинге Screencast.com. Условно, потому что в бесплатной версии картинки могут храниться до года, а также ограничен трафик, поэтому, если разместите ссылку на картинку на общедоступном сайте, лимит трафика вы быстро исчерпаете, и картинка отображаться перестанет. Так что это сугубо для личного пользования или внутри небольшой рабочей группы. Размещается картинка/видео в одно нажатие, а после загрузки в ваш буфер обмена скопируется короткая ссылка на неё, которую вы можете использовать.
Видео в бесплатной версии сохраняется только в SWF (да, сейчас браузеры его блокируют, но можно для своих разрешить), но и этого вполне достаточно, если надо показать косяк в анимации или последовательность действий, при которых проявляется ошибка, когда одной картинки недостаточно.
В остальное время утилитка покоится в трее и не раздражает.
UPD: ребята из TechSmith, похоже, выпустили улучшенную версию под названием Snagit. Но она стоит 50 баксов, так что идут они лесом.
Продолжу серию об инструментах, которыми я пользуюсь ежедневно. На очереди Jing от TechSmith. Да, штука старая (выпускалась с 2007 по 2012 годы), но тем не менее своё нехитрое дело выполняет.
Очень удобный инструмент для захвата области экрана как в картинку, так и в видео. Пользуемся с коллегами постоянно, когда надо объяснить друг другу, где косяк, что надо исправить или как протестировать исправление (куда нажать и в какой последовательности). Программа позволяет захватить любую область экрана, нажатием сочетания клавиш по вашему выбору. Кстати, прога умеет определять границы окон и даже документов внутри окон, поэтому часто не нужно вымерять размеры до пикселя, она сама предложит заскринить нужную область. Затем в случае с картинкой можно добавить туда выделение, рамки, стрелки и текст (см. картинку ниже). Вполне достаточно, чтобы графически выразить свою мысль, а не писать целый абзац текста вроде «в выпадающем списке в верхнем левом углу выберите пункт XXX, отметьте флажок YYY и слева внизу страницы нажмите кнопку ZZZ».
Потом картинку можно как просто скопировать в буфер, так и сохранить локально или разместить на условно бесплатном хостинге Screencast.com. Условно, потому что в бесплатной версии картинки могут храниться до года, а также ограничен трафик, поэтому, если разместите ссылку на картинку на общедоступном сайте, лимит трафика вы быстро исчерпаете, и картинка отображаться перестанет. Так что это сугубо для личного пользования или внутри небольшой рабочей группы. Размещается картинка/видео в одно нажатие, а после загрузки в ваш буфер обмена скопируется короткая ссылка на неё, которую вы можете использовать.
Видео в бесплатной версии сохраняется только в SWF (да, сейчас браузеры его блокируют, но можно для своих разрешить), но и этого вполне достаточно, если надо показать косяк в анимации или последовательность действий, при которых проявляется ошибка, когда одной картинки недостаточно.
В остальное время утилитка покоится в трее и не раздражает.
UPD: ребята из TechSmith, похоже, выпустили улучшенную версию под названием Snagit. Но она стоит 50 баксов, так что идут они лесом.
👍2