Поделиться через


Руководство. Создание программ C# на основе файлов

Приложения на основе файлов — это программы, содержащиеся в одном *.cs файле, который создается и выполняется без соответствующего файла проекта .*.csproj Приложения на основе файлов идеально подходят для обучения C#, так как они имеют меньшую сложность: вся программа хранится в одном файле. Приложения на основе файлов также полезны для создания служебных программ командной строки. На платформах Unix можно запускать приложения на основе файлов с помощью #!директив (shebang). Изучив это руководство, вы:

  • Создайте файловую программу.
  • Добавьте поддержку Unix Shebang (#!).
  • Чтение аргументов командной строки.
  • Обработка стандартных входных данных.
  • Запись выходных данных искусства ASCII.
  • Обработка аргументов командной строки.
  • Используйте проанализированные результаты командной строки.
  • Протестируйте окончательное приложение.

Вы создаете файловую программу, которая записывает текст как искусство ASCII. Приложение содержится в одном файле, использует пакеты NuGet, реализующие некоторые основные функции.

Предпосылки

Создание файловой программы

  1. Откройте Visual Studio Code и создайте новый файл с именем AsciiArt.cs. Введите следующий текст:

    Console.WriteLine("Hello, world!");
    
  2. Сохраните файл. Затем откройте интегрированный терминал в Visual Studio Code и введите следующее:

    dotnet run AsciiArt.cs
    

При первом запуске этой программы dotnet узел создает исполняемый файл из исходного файла, сохраняет артефакты сборки во временной папке, а затем запускает созданный исполняемый файл. Этот интерфейс можно проверить, введя dotnet run AsciiArt.cs еще раз. На dotnet этот раз узел определяет, что исполняемый файл является текущим, и запускает исполняемый файл, не создавая его снова. Выходные данные сборки не отображаются.

В предыдущих шагах показано, что приложения на основе файлов не являются файлами скриптов. Они представляют собой исходные файлы C#, созданные с помощью созданного файла проекта во временной папке. Одна из строк выходных данных, отображаемых при создании программы, должна выглядеть примерно так (в Windows):

AsciiArt succeeded (7.3s) → AppData\Local\Temp\dotnet\runfile\AsciiArt-85c58ae0cd68371711f06f297fa0d7891d0de82afde04d8c64d5f910ddc04ddc\bin\debug\AsciiArt.dll

На платформах UNIX папка выходных данных похожа на следующую:

AsciiArt succeeded (7.3s) → Library/Application Support/dotnet/runfile/AsciiArt-85c58ae0cd68371711f06f297fa0d7891d0de82afde04d8c64d5f910ddc04ddc/bin/debug/AsciiArt.dll

Эти выходные данные сообщают о том, где размещаются временные файлы и выходные данные сборки. В этом руководстве в любое время, когда вы редактируете исходный файл, dotnet узел обновляет исполняемый файл перед его запуском.

Приложения на основе файлов — это обычные программы C#. Единственное ограничение заключается в том, что они должны быть записаны в один исходный файл. Операторы верхнего уровня или классический Main метод можно использовать как точку входа. Можно объявить любые типы: классы, интерфейсы и структуры. Алгоритмы можно структурировать в файловой программе так же, как и в любой программе C#. Можно даже объявить несколько пространств имен для упорядочивания кода. Если программа на основе файлов растет слишком большой для одного файла, ее можно преобразовать в программу на основе проекта и разделить источник на несколько файлов. Приложения на основе файлов — это отличный инструмент прототипа. Вы можете начать экспериментировать с минимальными затратами, чтобы доказать концепции и алгоритмы сборки.

Поддержка Unix Shebang (#!)

Замечание

#! Поддержка директив применяется только на платформах UNIX. Для Windows нет аналогичной директивы для непосредственного выполнения программы C#. В Windows необходимо использовать dotnet run в командной строке.

В unix можно напрямую запускать приложения на основе файлов, введя имя исходного файла в командной строке вместо dotnet run. Необходимо внести два изменения:

  1. Задайте разрешения на выполнение исходного файла:

    chmod +x AsciiArt.cs
    
  2. Добавьте директиву shebang (#!) в качестве первой строки AsciiArt.cs файла:

    #!/usr/local/share/dotnet/dotnet run
    

Расположение dotnet может отличаться в разных установках unix. Используйте команду which dotnet, чтобы найти хост в вашей dotnet среде.

Кроме того, можно использовать #!/usr/bin/env dotnet для автоматического разрешения пути dotnet из переменной среды PATH:

#!/usr/bin/env dotnet

После внесения этих двух изменений вы можете запустить программу из командной строки напрямую:

./AsciiArt.cs

Если вы предпочитаете, вы можете удалить расширение, чтобы вместо этого ввести ./AsciiArt его. Вы можете добавить его в исходный #! файл, даже если используется Windows. Командная строка Windows не поддерживается #!, но компилятор C# разрешает директиву в приложениях на основе файлов на всех платформах.

Чтение аргументов командной строки

Теперь напишите все аргументы в командной строке в выходные данные.

  1. Замените текущее содержимое AsciiArt.cs следующим кодом:

    if (args.Length > 0)
    {
        string message = string.Join(' ', args);
        Console.WriteLine(message);
    }
    
  2. Чтобы запустить эту версию, введите следующую команду:

    dotnet run AsciiArt.cs -- This is the command line.
    

    Параметр -- указывает, что все следующие аргументы команд должны передаваться в программу AsciiArt. Аргументы This is the command line. передаются в виде массива строк, где каждая строка — одно слово: This, is, theи commandline..

Эта версия демонстрирует следующие новые понятия:

  • Аргументы командной строки передаются программе с помощью предопределенной переменной args. Переменная args представляет собой массив строк: string[] Если длина args равна 0, это означает, что аргументы не были предоставлены. В противном случае каждое слово в списке аргументов хранится в соответствующей записи в массиве.
  • Метод string.Join объединяет несколько строк в одну строку с указанным разделителем. В этом случае разделитель является одним пробелом.
  • Console.WriteLine записывает строку в стандартную выходную консоль, за которой следует новая строка.

Обработка стандартных входных данных

Это правильно обрабатывает аргументы командной строки. Теперь добавьте код для обработки входных данных из стандартных входных данных (stdin) вместо аргументов командной строки.

  1. Добавьте следующее else предложение в инструкцию if , добавленную в предыдущем коде:

    else
    {
        while (Console.ReadLine() is string line && line.Length > 0)
        {
            Console.WriteLine(line);
        }
    }
    

    Предыдущий код считывает входные данные консоли до тех пор, пока не будет прочитана пустая строка или чтение null . (Метод возвращает, Console.ReadLinenull если входной поток закрыт, введя ctrl+C.)

  2. Проверьте стандартные входные данные, создав текстовый файл в той же папке. Назовите файл input.txt и добавьте следующие строки:

    Hello from ...
    dotnet!
    
    You can create
    file-based apps
    in .NET 10 and
    C# 14
    
    Have fun writing
    useful utilities
    

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

  3. Снова запустите программу.

    С bash:

    cat input.txt | dotnet run AsciiArt.cs
    

    Или с помощью PowerShell:

    Get-Content input.txt | dotnet run AsciiArt.cs
    

Теперь программа может принимать аргументы командной строки или стандартные входные данные.

Запись выходных данных ASCII Art

Затем добавьте пакет, поддерживающий искусство ASCII, Colorful.Console. Чтобы добавить пакет в файловую программу, используйте директиву #:package .

  1. Добавьте следующую директиву после директивы #! в файле AsciiArt.cs:

    #:package Colorful.Console@1.2.15
    

    Это важно

    Последняя версия 1.2.15 пакета была последней Colorful.Console версией пакета, когда это руководство было последнее обновление. Проверьте страницу NuGet пакета для последней версии, чтобы убедиться, что вы используете версию пакета с последними исправлениями безопасности.

  2. Измените строки, вызываемые Console.WriteLine методом Colorful.Console.WriteAscii , вместо этого:

    async Task WriteAsciiArt(AsciiMessageOptions options)
    {
        foreach (string message in options.Messages)
        {
            Colorful.Console.WriteAscii(message);
            await Task.Delay(options.Delay);
        }
    }
    
  3. Запустите программу, и вы увидите выходные данные искусства ASCII вместо эхо-текста.

Параметры команды обработки

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

  1. Процитировать несколько слов, которые должны быть написаны на одной строке:

    AsciiArt.cs "This is line one" "This is another line" "This is the last line"
    
  2. Добавьте параметр для приостановки между каждой --delay строкой:

    AsciiArt.cs --delay 1000
    

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

Большинство приложений командной строки должны анализировать аргументы командной строки для эффективной обработки параметров, команд и ввода пользователем. Библиотека System.CommandLine предоставляет комплексные возможности для обработки команд, вложенных команд, параметров и аргументов, что позволяет сосредоточиться на том, что делает приложение, а не механика синтаксического анализа входных данных командной строки.

Библиотека System.CommandLine предлагает несколько ключевых преимуществ:

  • Автоматическое создание и проверка текста справки.
  • Поддержка соглашений о командной строке POSIX и Windows.
  • Встроенные возможности завершения вкладок.
  • Согласованное поведение синтаксического анализа между приложениями.
  1. System.CommandLine Добавьте пакет. Добавьте эту директиву после существующей директивы пакета:

    #:package System.CommandLine@2.0.0
    

    Это важно

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

  2. Добавьте необходимые инструкции using в верхней части файла (после #! и #:package директив):

    using System.CommandLine;
    using System.CommandLine.Parsing;
    
  3. Определите параметр задержки и аргумент сообщений. Добавьте следующий код для создания CommandLine.Option и CommandLine.Argument объектов для представления параметра командной строки и аргумента:

    Option<int> delayOption = new("--delay")
    {
        Description = "Delay between lines, specified as milliseconds.",
        DefaultValueFactory = parseResult => 100
    };
    
    Argument<string[]> messagesArgument = new("Messages")
    {
        Description = "Text to render."
    };
    

    В приложениях командной строки параметры обычно начинаются с -- (двойной дефис) и могут принимать аргументы. Параметр --delay принимает целый аргумент, указывающий задержку в миллисекундах. Определяет messagesArgument , как все остальные маркеры после анализа параметров анализируются как текст. Каждый маркер становится отдельной строкой в массиве, но текст может быть кавычек, чтобы включить несколько слов в один маркер. Например, "This is one message" становится одним маркером, а This is four tokens становится четырьмя отдельными маркерами.

    Предыдущий код определяет тип аргумента для --delay параметра и что аргументы являются массивом значений string . Это приложение имеет только одну команду, поэтому вы используете корневую команду.

  4. Создайте корневую команду и настройте ее с помощью параметра и аргумента. Добавьте аргумент и параметр в корневую команду:

    RootCommand rootCommand = new("Ascii Art file-based program sample");
    
    rootCommand.Options.Add(delayOption);
    rootCommand.Arguments.Add(messagesArgument);
    
  5. Добавьте код для синтаксического анализа аргументов командной строки и обработки ошибок. Этот код проверяет аргументы командной строки и сохраняет проанализированные аргументы в объекте System.CommandLine.ParseResult :

    ParseResult result = rootCommand.Parse(args);
    foreach (ParseError parseError in result.Errors)
    {
        Console.Error.WriteLine(parseError.Message);
    }
    if (result.Errors.Count > 0)
    {
        return 1;
    }
    

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

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

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

  1. record Добавьте объявление для хранения сообщений и значения параметра задержки:

    public record AsciiMessageOptions(string[] Messages, int Delay);
    
  2. Добавьте следующую локальную функцию перед объявлением записи. Этот метод обрабатывает аргументы командной строки и стандартные входные данные и возвращает новый экземпляр записи:

    async Task<AsciiMessageOptions> ProcessParseResults(ParseResult result)
    {
        int delay = result.GetValue(delayOption);
        List<string> messages = [.. result.GetValue(messagesArgument) ?? Array.Empty<string>()];
    
        if (messages.Count == 0)
        {
            while (Console.ReadLine() is string line && line.Length > 0)
            {
                Colorful.Console.WriteAscii(line);
                await Task.Delay(delay);
            }
        }
        return new([.. messages], delay);
    }
    
  3. Создайте локальную функцию для записи искусства ASCII с указанной задержкой. Эта функция записывает каждое сообщение в записи с указанной задержкой между каждым сообщением:

    async Task WriteAsciiArt(AsciiMessageOptions options)
    {
        foreach (string message in options.Messages)
        {
            Colorful.Console.WriteAscii(message);
            await Task.Delay(options.Delay);
        }
    }
    
  4. Замените if предложение, написанного ранее, следующим кодом, который обрабатывает аргументы командной строки и записывает выходные данные:

    var parsedArgs = await ProcessParseResults(result);
    
    await WriteAsciiArt(parsedArgs);
    return 0;
    

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

Тестирование окончательного приложения

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

#!/usr/local/share/dotnet/dotnet run

#:package Colorful.Console@1.2.15
#:package System.CommandLine@2.0.0

using System.CommandLine;
using System.CommandLine.Parsing;

Option<int> delayOption = new("--delay")
{
    Description = "Delay between lines, specified as milliseconds.",
    DefaultValueFactory = parseResult => 100
};

Argument<string[]> messagesArgument = new("Messages")
{
    Description = "Text to render."
};

RootCommand rootCommand = new("Ascii Art file-based program sample");

rootCommand.Options.Add(delayOption);
rootCommand.Arguments.Add(messagesArgument);

ParseResult result = rootCommand.Parse(args);
foreach (ParseError parseError in result.Errors)
{
    Console.Error.WriteLine(parseError.Message);
}
if (result.Errors.Count > 0)
{
    return 1;
}

var parsedArgs = await ProcessParseResults(result);

await WriteAsciiArt(parsedArgs);
return 0;

async Task<AsciiMessageOptions> ProcessParseResults(ParseResult result)
{
    int delay = result.GetValue(delayOption);
    List<string> messages = [.. result.GetValue(messagesArgument) ?? Array.Empty<string>()];

    if (messages.Count == 0)
    {
        while (Console.ReadLine() is string line && line.Length > 0)
        {
            // <WriteAscii>
            Colorful.Console.WriteAscii(line);
            // </WriteAscii>
            await Task.Delay(delay);
        }
    }
    return new([.. messages], delay);
}

async Task WriteAsciiArt(AsciiMessageOptions options)
{
    foreach (string message in options.Messages)
    {
        Colorful.Console.WriteAscii(message);
        await Task.Delay(options.Delay);
    }
}

public record AsciiMessageOptions(string[] Messages, int Delay);

В этом руководстве вы узнали, как создать файловую программу, в которой вы создаете программу в одном файле C#. Эти программы не используют файл проекта и могут использовать директиву #! в системах UNIX. Учащиеся могут создавать эти программы после работы с нашими онлайн-учебниками и перед созданием более крупных приложений на основе проектов. Приложения на основе файлов также являются отличной платформой для служебных программ командной строки.