Интерполяция строк в C#
В этом руководстве описано, как использовать интерполяцию строк для форматирования и включения результатов выражения в строку результатов. В примерах предполагается, что вам знакомы основные принципы C# и форматирования типов .NET. Если вы не знакомы с интерполяцией строк или форматированием типов .NET, сначала ознакомьтесь с интерактивным учебником по интерполяции строк. Дополнительные сведения о форматировании типов в .NET см. в разделе Типы форматирования в .NET.
Примеры C# в этой статье выполняются во встроенном средстве выполнения кода и на площадке Try.NET. Нажмите на кнопку Выполнить, чтобы выполнить пример в интерактивном окне. После выполнения кода вы можете изменить его и выполнить измененный код, снова нажав на кнопку Выполнить. Либо в интерактивном окне выполняется измененный код, либо, если компиляция завершается с ошибкой, в интерактивном окне отображаются все сообщения об ошибках компилятора C#.
Введение
Функция интерполяции строк создана на основе функции составного форматирования и имеет более удобный синтаксис для включения форматированных результатов выражения в строку результатов.
Для определения строкового литерала в качестве интерполированной строки добавьте к началу символ $ . Вы можете внедрить любое допустимое выражение C#, возвращающее значение в интерполированной строке. В следующем примере после вычисления выражения его результат преобразуется в строку и включается в строку результатов:
Как показано в примере, можно включить выражение в интерполированную строку, заключив его в фигурные скобки:
Интерполированные строки поддерживают все возможности составного форматирования строк. Это делает их более удобочитаемыми по сравнению с использованием метода String.Format.
Как указать строку формата для выражения интерполяции
Задайте строку формата, которая поддерживается типом результата выражения, указав ее после выражения интерполяции через двоеточие:
В следующем примере показано, как задать стандартные и настраиваемые строки формата для выражений, возвращающих дату и время или числовые результаты:
Дополнительные сведения см. в разделе Компонент строки формата в статье Составное форматирование. Этот раздел содержит ссылки на разделы, описывающие стандартные и настраиваемые строки формата, поддерживаемые базовыми типами .NET.
Управление шириной поля и выравниванием в форматированных выражениях интерполяции
Задайте минимальную ширину поля и выравнивание форматированного результата выражения, указав константное выражение после выражения интерполяции через запятую:
Если значение alignment положительное, форматированное выражение будет выровнено по правому краю, а если отрицательное — по левому.
Если вам нужно задать и выравнивание, и строку формата, начните с компонента выравнивания:
В следующем примере показано, как задать выравнивание. Текстовые поля разграничены символом вертикальной черты («|»):
В выходных данных в примере видно, что если длина форматированного результата выражения превышает заданную ширину поля, значение alignment игнорируется.
Дополнительные сведения см. в разделе Компонент выравнивания в статье Составное форматирование.
Как использовать escape-последовательности в интерполированной строке
Интерполированные строки поддерживают все escape-последовательности, которые могут использоваться в обычных строковых литералах. Дополнительные сведения см. в статье Escape-последовательности строки.
Для литеральной интерпретации escape-последовательности используйте строковый литерал verbatim. Интерполированная строка verbatim начинается с символа $ , за которым следует символ @ . Начиная с C# 8.0 маркеры $ и @ можно использовать в любом порядке: $@". " и @$". " являются допустимыми интерполированными строками verbatim.
В строке результатов указывайте двойную фигурную скобку «<<" или ">>». Дополнительные сведения см. в разделе escape-скобки в статье Составное форматирование.
В следующем примере показано, как включить фигурные скобки в строку результата и создать интерполированную строку verbatim:
Как использовать троичный условный оператор ?: в выражении интерполяции
Двоеточие (:) имеет особое значение в элементе выражения интерполяции. Чтобы использовать условный оператор в выражении, заключите это выражение в скобки, как показано в следующем примере:
Создание строки результата с интерполяцией для определенного языка и региональных параметров
По умолчанию в интерполированной строке используется текущий язык и региональные параметры, определяемые свойством CultureInfo.CurrentCulture для всех операций форматирования. Используйте неявное преобразование интерполированной строки для экземпляра System.FormattableString и вызовите метод ToString(IFormatProvider), чтобы создать строку результата с определенным языком и региональными параметрами. Следующий пример показывает, как это сделать:
Как показано в примере, можно использовать один экземпляр FormattableString для создания нескольких строк результата для различных языков и региональных параметров.
Как создать строку результата с помощью инвариантного языка и региональных параметров
Наряду с методом FormattableString.ToString(IFormatProvider) можно использовать статический метод FormattableString.Invariant, чтобы разрешить интерполированную строку в строке результата для InvariantCulture. Следующий пример показывает, как это сделать:
Заключение
В этом руководстве описаны распространенные сценарии использования интерполяции строк. Дополнительные сведения об интерполяции строк см. в разделе Интерполяция строк. Дополнительные сведения о форматировании типов в .NET см. в разделах Типы форматирования в .NET и Составное форматирование.
Интерполяция + (линейная | логарифмическая) шкала + С++
Понадобилось мне как-то сделать интерфейс для загрузки в микроконтроллер график функции «сопротивление -> температура» (график решили задавать по нескольким точкам, а потом их интерполировать). По ходу дела выяснилось, что график будет оч-чень нелинейным (180 Ом -> 100 o , 6 000 Ом -> 0 o , 30 000 Ом -> -30 o ). Поэтому пришлось мне погрузиться в тему логарифмических шкал… и сразу вынырнуть, так как я не нашел того, что мне нужно. А нужно-то мне было всего лишь понять математику (и реализацию на С++) таких дел. ЧуднО — вроде такая нужная тема, а не расписано! Ну да ладно — мозги заскрипели и вспомнили высшую математику из университета, и программа была написана. Решил описать я свои мытарства тут — может, кому пригодится.
В этой статье я распишу теорию (а также базовые виртуальные классы), в следующей возьмусь за конкретные реализации средствами Qt.
Осторожно: в тексте много графики!
Откуда растут ноги у задачи
В общем, надо мне сделать регулятор холостого хода — такая штука для автомобиля, которая при холостом ходе в зависимости от температуры двигателя должна поддерживать определенные обороты. Поддерживает она их, регулируя заслонку шаговым двигателем.
В общем, мне надо знать текущую температуру. Было решено измерять ее штатными средствами — с терморезистора. Измеряем падение напряжения на нем — получаем сопротивление. Далее из таблицы (поскольку это микроконтроллер) получаем требуемые обороты.
Таблицу эту надо задать (для этого программа пишется средствами Qt). У меня есть несколько точек «сопротивление => температура». Мне надо для каждого кода АЦП (для ряда значений сорпотивлений) получить соответствующую температуру. Поскольку у разных автомобилей эти значения могут быть разными, то надо на экране, сверившись с таблицей, задать несколько точек на кривой.
По ходу дела оказалось, что график этот будет явно в логарифмическом масштабе. Значит, надо его вывести на экран. Как это сделать — читаем дальше.
Постановка задачи
Давайте немного подробнее опишем что нам надо:
- задание функции — необходимо задать несколько точек, по которым строится график. Вобщем, вспоминаем интерполяцию;
- построение графика функции — да-да, я знаю про Qwt. Может, не очень хорошо я его знаю, т. к. я не нашел в нем следующей возможности:
- интерактивное задание функции — мне нужно двигать точки, по которым строится функция, прямо на экране, в текущих экранных координатах шкалы, которые переводятся в реальное значение;
- линейная/логарифмическая шкала — поскольку значения вот такие, как я написал, мне пришлось заложить возможность менять шкалу. Причем как одну, так и обе сразу.
Вот такое ТЗ… Ну да ничего, я справился! Давайте и вам помогу.
Да, пока не нырнули — спасибо Equation Editor-у от CodeCogs! С их помощью я лихо построил все математические формулы без всяких Microsoft Equation Editor, которые потом надо еще экспортировать в графику со вставкой сюда. Кстати, там есть и русский редактор. В общем, рекомендую!
Ну и если вместо формул вы видите пустые квадратики — это тоже «спасибо» Equation Editor-у…
Прикрепленный Excel-файл
По ходу написания статьи я все расчеты строил и проверял в таблице Excel с формулами. Оказалось очень удобно. И я решил его выложить для общественного пользования. Там внизу перечислены страницы по разделам. На каждой странице параметры, которые можно менять, отмечены как ячейки с желтым фоном. Остальные клетки лучше не трогать. Впрочем, все формулы можно смело смотреть. Скачивайте файлик и пробуйте на здоровье! Если проблемы с файлом — пишите, вышлю.
Функциональная зависимость
Итак, у нас есть некоторая зависимость — обозначим ее как
. Здесь у нас
— горизонтальная ось графика,
— вертикальная. В моем случае
было значение сопротивления,
— температура.
Почему не
? Ведь вроде бы должно быть так? Так-то оно так, но только в школе в простейшем случае.
— это координаты точки на плоскости. Для простоты определимся использовать Декартову систему координат: задает вертикальное смещение горизонтальной оси относительно нуля,
задает горизонтальное смещение вертикальной оси относительно нуля.
Все хорошо тогда, когда мы рисуем на бумаге эту самую систему координат и в ней ставим точки. Там и вправду — выбрали центр, линейкой отложили сюда, потом туда. А вот при построении графика в какой-то программе уже тонкости начинаются — что считать нулем? Что считать за «+», а что за «-«? Я рисую для этой статьи графику в CorelDRAW — там центр считается снизу слева (его можно передвинуть куда надо).
Да и в каких единицах график-то? В сантиметрах? А почему? У меня следующий этап будет реализация на С++ средствами Qt, так там я сделаю окно QWidget, у которого по умолчанию ноль — это слева сверху; единицы измерения — экранные пиксели.
Ну и не забываем о том, что это все эти красивые рассуждения справедливы пока что для линейной шкалы, а у нас маячит за горизонтом логарифмическая. Там вообще черт знает что будет!
Но это только лишь точка. А у нас будет какая-то линия, точнее — много линий. Что там будут за преобразования?
Вот именно поэтому мы с самого начала должны четко разделить функциональную зависимость и преобразования координат.
Итак, давайте договоримся о следующем: у нас есть некоторый абстрактный процесс, который описывается функциональной зависимостью
. При отображении на экран используется преобразование в координаты , где
,
. Следующие шаги — это прояснить эти самые
и
.
Но отложим пока в сторону координаты — нам надо как-то задать нашу функцию (помните ТЗ)? Причем задать в тех самых абстрактных координатах
. Этим и займемся.
Интерполяция
В моем случае был известен ряд точек
:
, Ω |
, ˚ |
|---|---|
| 180 | 100 |
| 6 000 | 0 |
| 30 000 | -30 |
Не ахти какая сложна и большая таблица, но тут явно куча пустых мест. А какое сопротивление соответствует 60˚, -40˚, . В общем, надо проставить отсутствующие точки. И в этом нам помогут интерполяция, аппроксимация и экстраполяция. Впрочем, не пугайтесь — одной интерполяции хватит за глаза.
Методов интерполяции много, все рассматривать я тут не буду. Лично мне приглянулся вначале интерполяционный многочлен Лагранжа. Он весьма прост в расчете и реализации, а также в настройке. Там предполагается, что задано множество из
точек вида (тут мы на время таки вернемся к заданию точек в виде
— так уж принято в математике).
Многочлен вычисляется как , где .
Математика испугала? Хм… Ладно, напишу на языке С++:
Как видите, все достаточно тривиально (насколько тривиальными могут быть полиномы).
Еще одно большое достоинство полиномов Лагранжа — их легко можно промоделировать в таблице Excel-я, что я и делал.
Потом, правда, все стало немного печально, т. к. у этих полиномов, как и у любых других, на графике видны вибрации. Т. е. они не могут дать прямые линии — постоянные значения. В моем случае я не смог их настроить дОлжным образом — они выгибались в явно недопустимые числа. Поэтому мне пришлось от них отказаться…
Работая в Corel, я был близко знаком с кривыми Безье — тоже достаточно удобное и простое представление табличных данных. Весьма легко реализуется в программировании. Однако это уже не интерполяция, а, скорее, аппроксимация, т. к. тут приходится подгонять кривую к нужному виду.
В итоге, внимательно присмотревшись к своей функции, я понял, что у меня вполне прокатит кусочно-линейная интерполяция — прямые отрезки между заданными линиями. Не то, чтобы совсем уж фен-шуйно, но зато легко реализуемо и удобно настраиваемо.
Говоря языком математики, мы между точками и проводим прямые линии вида .
Опять же, на языке С++ это будет выглядеть так:
Тоже ничего революционного, не так ли?
Есть одно существенное различие между полиномом Лагранжа и линейной интерполяцией: у первого нельзя явно задать значения за пределами точек — они вычисляются, у второго можно это дело контролировать. Также и поэтому я в конечном итоге остановился на линейном варианте. Более того — в логарифмическом масштабе, к которому я стремился, линейные отрезки дают более подходящий мне вариант.
Впрочем, не будем заморачиваться сейчас на методах интерполяции. Давайте лучше мы сделаем базовый класс, от которого будем наследовать реализации различных методов $#*@!поляции.
Базовый класс для задания/расчета функции
Что этот класс должен уметь делать? Мне кажется, что такой класс должен:
- давать значение функции в зависимости от аргумента — собственно, ради чего мы его и городим;
- реагировать (перемещать и пересчитывать) на изменение точек интерполяции — на вход подается факт нажатия/отпускания в некоторой координате, в результате чего производится пересчет параметров;
- различать одиночный и двойной щелчок мышки — одиночное нажатие, как по мне, указывает на передвижение точки; двойное создает новую точку;
- прорисовывать точки интерполяции как при их движении, так и без оного — т. к. у разных методов интерполяции будет разное интуитивно понятное значение точек интерполяции, то и выводить их должен производный класс (например, в интерполяции точка является частью графика; в аппроксимации точка не обязательно лежит на графике; в кривых Безье часть точек лежит на графике, часть задают форму);
- давать координаты текущей передвигаемой точки — это нужно для вывода текста координаты данной точки;
- давать сервисную информацию — например, «определена ли функция?», «сколько точек для интерполяции используется?», «получить координаты точек» и т. п. Эти данные позволят сохранить текущие настройки;
- производить настройку — «распределить столько-то точек», «задать координаты точки» — это нам позволит восстановить сохраненные настройки.
Еще есть мысли? Если будут — пишите в комментариях, добавим!
Получается такой вот класс:
(Тем, кто недоволен моим стилем и структурой — предложите объективно лучше!)
(Тем, кто найдет ошибки в коде — спасибо!)
Думаю, тут все очевидно.
Для координат используется представление точки в виде QPointF (пара чисел в виде qreal, qreal. «На всех платформах, кроме ARM, используется double» — так написано для Qt 4.8).
Нажатия на кнопки мышки реализованы функциями MouseClicked , MouseDblClicked , MouseReleased и MouseMove . Предполагается, что в конкретных реализациях будут соответствующие реакции.
Для прорисовки точек используются методы DrawPoints и DrawCurPoint . Если для всех методов, кроме этих, координаты используются абстрактные, то тут нужны самые что ни на есть экранные. Поэтому сюда передаются два объекта класса ScaleBase для преобразований. Этот класс — тоже виртуальный. Его предки реализуют преобразование из абстрактных координат в текущие экранные. Сам же этот класс будет описан ниже.
Текущее значение функции возвращает метод f (const qreal) и перегруженная операторная функция operator() (const qreal) .
Для задания структуры используются функции set_points (Num) — задание количества точек, point (Num) , set_point (Num) , get_point (Num) — задание координат конкретной точки. num_points () const — возвращает количество точек, point (Num) const , get_point (Num) const возвращают координаты точки. is_specified () const возвращает true , если структура функции задана.
В следующей статье мы распишем пару вариантов реализации этого класса.
Функция преобразования для вертикальной/горизонтальной шкалы
Есть линейные и логарифмические шкалы. Учитывая, что вертикальная шкала может быть сделана в одном формате, а горизонтальная — в другом, мы получаем четыре варианта графика:

Вариант первый — обе шкалы линейные. Вариант второй — обе логарифмические. Варианты третий и четвертый — смешанные графики. Кстати, в моем случае именно смешанный случай в итоге и подошел, т. к. по горизонтали у меня потребовался логарифмический масштаб, по вертикали — линейный.
Следовательно, задачу отображения нужно решать отдельно для обеих осей.
Напомним, что при отображении на экран используется преобразование в координаты , где
,
. Наша дальнейшая задача — построить эти функции для линейного и логарифмического случаев.
Что это за функции такие? На вход они получают координату в абстрактных (для компьютерной подпрограммы отображения на экран) координатах, на выход дают в экранных («экранные» координаты будут для разных операционных систем разными»). Для расчета им нужно знать следующее:

- пределы абстрактных координат — те предельные значения аргумента и функции, что нас интересуют. Это будут
,
для горизонтальной оси и
,
для вертикальной. Не нужно допускать классическую ошибку:
,
! В приведенном примере это проиллюстрировано; - пределы экранных координат — границы картинки, в которой рисуется график. Естественно, в текущих экранных координатах. На графике это
,
для горизонтальной оси и
,
для вертикальной;
- шаг экранной координаты — текущий шаг пикселя
,
. В простом случае это будет единица. Но в Qt вертикальный ноль — это верх окна. Следовательно,
. А еще могут быть применены всякие трансформации, и шаг уже будет отнюдь не единичным.
Обратите внимание — для задачи преобразования не важно, вертикальная это ось или нет! Она (задача) оперирует граничными значения входного, выходного, а также шагом выходного параметров. Следовательно, задачу можно обобщить: необходимо преобразовать параметр на основе его пределов , в выходную величину с учетом ее пределов , и шага . Тут намеренно введены обозначения , вместо привычных alt=»image» />, alt=»image» />, т. к. иначе будет путаница. Одно существенное дополнение: .
Базовый класс для преобразований шкал
Давайте сформулируем желаемую функциональность виртуального класса преобразований для шкалы, от которого будут унаследованы реализации шкал:
- преобразований из экранных координат в абстрактные и наоборот — логично, ради этого мы его и делаем;
- настройка преобразования — это тоже логично;
- свойства — текущие свойства преобразования (минимальные/максимальные значения, шаг для разных значений);
- информация по сетке — позиции для крупной и мелкой сетки, подписи под шкалой.
Реализация может выглядеть так:
Настройка шкалы производится функциями set_. (Val) . Пересчет необходимых значений должен производится в этих же функциях. При изменении размера окна вызывается метод Resized (Size) .
Для повышения производительности можно один раз рассчитать соответствие точки на экране и ее значение в исходных, абстрактных координатах. Этот массив возвращается методом scr_values () const . Далее, для построения крупной и мелкой сетки рассчитываются массивы (возвращают их, соответственно, функции scr_maj_grid () и scr_min_grid () ). Длина массива соответствует количеству оных линий, значение — смещению относительно начала шкалы на экране (т. е. индексу первого массива). Также заранее рассчитываются два массива — текст подписи к шкале (функция scr_text_str () ) и смещения этих подписей относительно начала (функция scr_text_pos () ).
Наконец, прямое преобразование — из абстрактных в экранные координаты — производится функцией scr (Val) , обратное — функцией val (Scr) .
Линейное преобразование
Давайте отдельно рассмотрим линейное преобразование для горизонтальной и вертикальной оси.


Мы имеем некоторую функцию — кривую в одном представлении. Для другого нам пришлось ее сузить и сместить вправо (окно на экране уменьшили и сдвинули вправо). Для другого представления нам пришлось сместить ее влево (окно сдвинули влево). Как это описывается математически? Достаточно просто: alt=»image» />. В первом случае, похоже, , во втором alt=»image» />.
В другом случае нам пришлось сузить вертикальное представление кривой и сдвинуть вверх. А потом и вообще — перевернуть. Оба этих преобразования описываются как alt=»image» />. В первом случае , . Во втором случае alt=»image» />.

Оба преобразования имеют одно и то же математическое описание: . В данном описании есть две константы, которые определяют преобразование — alt=»image» />и alt=»image» />. Первая определяет угол наклона, вторая — смещение относительно нуля.
Расчет этих констант достаточно прост — это решение системы двух уравнений:
Еще важно уметь делать обратное преобразование — скажем, координаты указателя мыши перевести в абстрактные координаты. Также ничего сложного:
Шаг в данном случае для расчета не используется, но он потом нам пригодится в реализации на С++ для расчета смещения.
Как это будет использоваться на практике? Да все просто! Горизонтальное преобразование: — граница картинки графика, соответствующая (как правило, слева), — (как правило, справа), — шаг вывода картинки по горизонтали. Вертикальное преобразование — аналогично, но по вертикали (у нас в Qt будет нижней границей картинки, — верхней, причем ).
Логарифмическое преобразование
А теперь окунемся туда, ради чего все это закрутилось:

(на графике не логарифм нарисован, а что-то похожее на него. Сделано это специально, т. к. логарифм тут будет не очень нагляден)
Если один и тот же на всей шкале, то будет везде разным! По какому закону он меняется? Правильно — по логарифмическому! Давайте первым делом научимся определять этот самый .
Всего точек у нас будет (например, , , ; тогда у нас будет 3 точки). Этому соответствует диапазон входных значений . Значению соответствует , соответствует . Последняя точка имеет индекс . Чему соответствует ?
Для линейной шкалы , где определяется диапазоном входных значений. Тут можно поступить аналогично, только вместо умножения будет возведение в степень: (помним, что ). Есть один, правда, ньюанс: при у нас , а должно бы. Это решается просто — вычтем единицу: . И тогда для нулевого случая все сходится. Как рассчитать в данном случае? Есть разные варианты. Я это предпочитаю сделать следующим образом.
Нам известно, что . В то же время мы теперь знаем, что . Получается уравнение: . Решим его относительно : . Вспомнив, чему равно и заменив корень возведением в степень, получим приемлемый для компьютера вид: (напоминаю, что ). Прекрасно, базовая величина получена!
Фактически, мы получили алгоритм преобразования из экранной координаты в абстрактную — обратная задача. Теперь режим прямую задачу — преобразование из абстрактной координаты в экранную. Задача решается несложно. Фактически, нужно найти , а для него несложно и .
Для нахождения надо решить уравнение относительно : . Ну а дальше уже из свойств логарифма получим, что . Ну и далее, рассматривая экранные координаты как линию с наклоном, получим, что (для полной красоты надо еще заменить на ).
Вроде бы базовую математику рассмотрели. Нашли ошибки или неточности — пишите в комментариях, буду благодарен!
Со временем напишу следующую статью — реализацию этой математики средствами Qt языка C++.
Что значит $ перед строкой в C# | Интерполяция строк c#
Наверняка, вы видели символ $ перед строкой. Этот знак говорит о том, что используется интерполяция строк c#. Этот механизм позволяет максимально удобным образом выполнять форматирование текста. Разумеется, для тех же целей можно использовать String.Format.
Рассмотрим на коротком примере, что удобнее.
![]()
Результат выполнения фрагмента кода
Так, результат выполнения не слишком отличается. Однако, интерполяция строк в определенной степени проще в использовании. К тому же, если мы модифицируем вызов String.Format следующим образом.

Исключение String.Format
Говоря проще, количество переменных, переданных методу String.Format, не соответствует изначально заданному. Из-за чего мы получили ошибку.
Интерполяция строк c# — интересные механизмы
Указание формата при интерполяции
Помимо удобства форматирования строки, мы можем использовать указание формата. Это можно делать для любых переменных. Особенно удобным этот механизм можно назвать при работе с датами. Так, мы можем отображать даты в соответствии с форматом региона конечного пользователя.

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

Результат выполнения фрагмента кода
Используя тернарный оператор в интерполированных строках, не забывайте оборачивать его в скобки. Как вы помните, двоеточие используется при указании формата, а потому без скобок компилятор поймет нас неправильно.
Интерполяция строк c# — Итого
Интерполяция строк c# — крайне мощный механизм, позволяющий работать с строками максимально удобным образом. По сути, этот инструмент может найти применение в любом приложении. А главное, интерполяция проста в изучении. И еще одним важным преимуществом её использования является увеличение читаемости кода и его понимания. Ведь код читают намного чаще, чем вносят в него изменения.
Кроме того, рекомендую прочитать статью String C# | Работа со строками в C#. А также подписывайтесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для программистов.