Visual Studio — это интегрированная среда разработки (IDE), разработанная Microsoft, которая поддерживает несколько языков программирования, таких как C++, C#, VB.NET, Python, JavaScript и т. д. Подходит для разработки настольных приложений, веб-сайтов, облачных сервисов и мобильных приложений.
Visual Studio поддерживает операционные системы Windows, а Visual Studio для Mac разработан для macOS.
[*.cs] indent_style = space indent_size = 43. После сохранения файла форматирование будет автоматически соответствовать этим правилам.
Вы можете проверить версию .NET SDK, установленную на вашем компьютере, с помощью инструмента командной строки:
dotnet --list-sdks
Если появится результат, подобный следующему, это указывает на установленную версию SDK:
8.0.100 [C:\Program Files\dotnet\sdk] 9.0.100-preview.3.24172.9 [C:\Program Files\dotnet\sdk]
Если версия, которую вы хотите использовать, не отображается, это означает, что она еще не установлена.
dotnetЗаказЕсли вы не видите эту опцию, вы также можете использовать меню «Пуск» Windows для поиска «Командной строки разработчика для VS», чтобы открыть ее.
Ctrl + `dotnetКоманда для проверки или работы SDKиспользование системы;
класс Программа {
статическая пустота Main() {
имя строки = «мир»;
Console.WriteLine($"Привет, {имя}!");
}
}
(x, y) => x + yvar q = list.Where(x => x > 10);await Task.Delay(1000);CS8618 означает: в конце конструктораНе обнуляемыйСвойства не инициализированы, поэтому компилятор C# предупреждает, что это может бытьnull。
продукт общественного класса {
общедоступная строка Имя {get; набор; } // Предупреждение CS8618
}
public class Product {
public string Name { get; set; }
public Product(string name) {
Name = name;
}
}
public class Product {
public string Name { get; set; } = string.Empty;
}
public class Product {
public string? Name { get; set; }
}
→ Применимо кNameразумно разрешено бытьnullситуация.
requiredМодификаторы (поддерживаются C# 11+)продукт общественного класса {
общедоступная обязательная строка Имя {get; набор; }
}
// Вызывающий должен быть инициализирован
var p = новый продукт {Name = «Мобильный телефон» }; // ХОРОШО
null:использоватьКонструкторилиrequired。null:Изменить наstring?。string.Empty)。floatиSystem::StringСложите, например:
System::String^ tmpStr = "Value: " + someFloat;
когдаsomeFloatЕсли он очень большой или очень маленький, он может автоматически отображаться в экспоненциальном формате (например, 1.23E+05).System::String::FormatилиToStringИспользуйте строку формата, чтобы указать количество десятичных знаков и избегать научной записи.использование пространства имен System;
значение с плавающей запятой = 123456,789f;
// Метод 1: String::Format
String^ tmpStr1 = String::Format("Значение: {0:F2}", value); // F2 представляет 2 цифры после десятичной точки
Консоль::WriteLine(tmpStr1);
// Метод 2: формат соответствия ToString
String^ tmpStr2 = "Значение: " + value.ToString("F2");
Консоль::WriteLine(tmpStr2);
//Метод 3: Больше цифр
String^ tmpStr3 = "Значение: " + value.ToString("F6");
Консоль::WriteLine(tmpStr3);
F2: фиксированный формат десятичной точки, 2 цифры после десятичной точки.F6: фиксированный формат десятичной точки, 6 цифр после десятичной точки.F{цифры}В С++/CLIgcnew System::String(stdStr.c_str())воляstd::string(обычноANSI / UTF-8кодирование) непосредственно в .NET.System::String,иSystem::StringОжиданиеUTF-16。
еслиstdStrЕсли он содержит символы, отличные от ASCII (например, китайские), символы будут отображаться искаженно.
marshal_as(предположение)Пространство имен должно быть заключено в кавычки:
#include <msclr/marshal_cppstd.h>
используя пространство имен msclr::interop;
std::string stdStr = "Китайский тест";
System::String^ netStr = marshal_as<System::String^>(stdStr);
✅ Этот метод позволяет автоматически правильно конвертировать UTF-8/ANSI в .NET Unicode.
---#include <codecvt>
#include <locale>
std::string utf8Str = u8"Китайский тест";
std::wstring WideStr = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>{}.from_bytes(utf8Str);
System::String^ netStr = gcnew System::String(wideStr.c_str());
---
std::wstringЕсли на стороне C++ изначально используются широкие строки символов, просто используйте их напрямую:
std::wstring wstr = L"Китайский тест";
System::String^ netStr = gcnew System::String(wstr.c_str());
---
std::stringИсходный код — UTF-8 (распространён в современных системах) → используйтеmarshal_asилиcodecvt。gcnew String(stdStr.c_str()), если только строка не является чисто английской.В .NET (такие языки, как C++/CLI, C#, VB.NET и т. д.)List<T>это общая категория коллекции,
Может хранить любые типы данных (T) и обеспечивать функцию динамического изменения размера.
оно принадлежитSystem::Collections::Genericпространство имен.
//Импортируем пространство имен
использование пространства имен System;
используя пространство имен System::Collections::Generic;
интервал основной()
{
// Объявляем список для хранения целых чисел
List<int>^ Numbers = gcnew List<int>();
//Добавляем элементы непосредственно во время инициализации
List<String^>^ имена = gcnew List<String^>({ "Алиса", "Боб", "Чарли" });
}
List<int>^ nums = gcnew List<int>();
//Добавляем новый элемент
числа->Добавить(10);
числа->Добавить(20);
числа->Добавить(30);
//Вставляем в указанную позицию
числа->Вставить(1, 15); // Вставляем 15 в индекс 1
//Удалить указанное значение
nums->Удалить(20);
//Удалить указанный индекс
nums->RemoveAt(0);
// Получаем элементы
целое значение = числа [1]; // Получаем элемент с индексом 1
// Проверяем, содержит ли он
если (числа->Содержит(30))
Console::WriteLine("Найдено 30");
// Очистить все элементы
числа->Очистить();
// для цикла
for (int i = 0; i < nums->Count; i++)
{
Console::WriteLine("{0}й элемент: {1}", i, nums[i]);
}
// для каждого цикла
для каждого (int n в числах)
{
Консоль::WriteLine(n);
}
| Свойства/методы | иллюстрировать |
|---|---|
Count | Текущее количество элементов |
Capacity | Внутренняя емкость (может увеличиваться автоматически) |
Add(item) | добавить элемент |
AddRange(collection) | Объединить все элементы другой коллекции |
Insert(index, item) | Вставить элемент в указанную позицию |
Remove(item) | Удаляет указанное значение (найдено первое совпадение) |
RemoveAt(index) | Удалить элемент по указанному индексу |
Clear() | Удалить все элементы |
Contains(item) | Проверьте, содержится ли указанное значение |
IndexOf(item) | Возвращает индекс элемента (-1, если он не существует) |
Sort() | Сортировочные элементы (для сопоставимых типов) |
Reverse() | Обратный порядок элементов |
10 15 30
В .NET (такие языки, как C++/CLI, C#, VB.NET и т. д.)Dictionary<TKey, TValue>это универсальная коллекция,
Используется для хранения «пар ключ-значение». Каждый ключ должен быть уникальным, а значение должно повторяться.
оно принадлежитSystem::Collections::Genericпространство имен.
//Импортируем пространство имен
использование пространства имен System;
используя пространство имен System::Collections::Generic;
интервал основной()
{
//Создаем словарь с ключом в виде целого числа и значением в виде строки
Dictionary<int, String^>^users = gcnew Dictionary<int, String^>();
// Прямая инициализация
пользователи->Добавить(1, «Алиса»);
пользователи->Добавить(2, «Боб»);
пользователи->Добавить(3, «Чарли»);
вернуть 0;
}
Dictionary<String^, int>^ age = gcnew Dictionary<String^, int>();
//Add new element
возраста->Добавить("Том", 25);
возраста->Добавить("Джерри", 30);
//Изменить значение (через индексатор)
возраст["Том"] = 26;
// Добавить или изменить (если ключ не существует, он будет добавлен автоматически)
возраст["Спайк"] = 40;
// удаляем элемент
age->Remove("Джерри");
//Доступ к элементам
if (возраст->ContainsKey("Том"))
{
Console::WriteLine("Возраст Тома — {0}", ages["Том"]);
}
// Попытаемся получить значение (избегаем исключений)
средний возраст;
if (возраст->TryGetValue("Spike", age))
{
Console::WriteLine("Возраст Спайка — {0}", age);
}
Dictionary<int, String^>^users = gcnew Dictionary<int, String^>();
пользователи->Добавить(1, «Алиса»);
пользователи->Добавить(2, «Боб»);
пользователи->Добавить(3, «Чарли»);
// Используем цикл KeyValuePair
для каждой (KeyValuePair<int, String^> записи в пользователях)
{
Console::WriteLine("Ключ = {0}, Значение = {1}", запись.Ключ, запись.Значение);
}
//Получаем только ключи
для каждого (ключ int в Users->Keys)
{
Console::WriteLine("Ключ: {0}", key);
}
//Получаем только значения
для каждого (имя String^ в пользователях->Значения)
{
Console::WriteLine("Имя: {0}", name);
}
| Свойства/методы | иллюстрировать |
|---|---|
Add(key, value) | Добавьте новый набор пар ключ-значение. |
Remove(key) | Удаляет указанный ключ и соответствующее ему значение. |
Clear() | Очистить все проекты |
ContainsKey(key) | Проверьте, существует ли указанный ключ |
ContainsValue(value) | Проверьте, существует ли указанное значение |
TryGetValue(key, out value) | Получите значение безопасно, не создавая исключения |
Count | Текущее количество элементов в словаре |
Keys | Возвращает коллекцию всех ключей |
Values | Возвращает коллекцию всех значений |
Ключ = 1, Значение = Алиса Ключ = 2, Значение = Боб Ключ = 3, Значение = Чарли Тому 26 лет Спайку 40 лет.
В .NET C++ вы можете использоватьSystem::IOПространства имен предоставляют функции для управления файлами и каталогами.
Получить последние файлы в каталоге можно, прочитав всю информацию о файле и сравнив дату последнего изменения.
Вот полный пример, показывающий, как получить последний файл в указанном каталоге:
#include "stdafx.h"
#include <iostream>
#include <cliext/vector>
#include <System.IO>
использование пространства имен System;
используя пространство имен System::IO;
использование пространства имен cliext;
интервал основной()
{
попробуй
{
//Указываем путь к каталогу
String^directoryPath = "C:\\Ваш\\Каталог\\Путь";
// Проверяем, существует ли каталог
если (!Directory::Exists(directoryPath))
{
Console::WriteLine("Каталог не существует: {0}", DirectoryPath);
вернуть -1;
}
// Получаем все файлы в каталоге
массив^ файлы = Directory::GetFiles(directoryPath);
// Если в каталоге нет файлов
если (файлы->Длина == 0)
{
Console::WriteLine("В каталоге нет файла.");
вернуть 0;
}
// Находим последний файл
Строка ^ newestFile = nullptr;
DateTime newestTime = DateTime::MinValue;
для каждого (файл String^ в файлах)
{
// Получаем время последней модификации файла
DateTime LastWriteTime = File::GetLastWriteTime(файл);
// Сравниваем время и обновляем последнюю информацию о файле
если (lastWriteTime > newestTime)
{
newestTime = LastWriteTime;
новыйФайл = файл;
}
}
// Вывод последней информации о файле
Console::WriteLine("Последний файл: {0}", newestFile);
Console::WriteLine("Время последнего изменения: {0}", newestTime);
}
поймать (Исключение^ ex)
{
Console::WriteLine("Произошла ошибка: {0}", ex->Message);
}
вернуть 0;
}
Directory::GetFilesМетод для получения всех путей к архивам в указанном каталоге.File::GetLastWriteTimeМетод для получения времени последнего изменения каждого файла.try-catchБлок перехватывает потенциальные исключения, например каталог, который не существует или недоступен.System.ReflectionЭто пространство имен в .NET Framework, которое предоставляет инструменты для проверки метаданных и управления ими, позволяя разработчикам динамически проверять типы, методы, свойства и т. д. во время выполнения, а также динамически создавать объекты и манипулировать ими.
Assembly: представляет загруженную сборку и предоставляет методы для загрузки, изучения и отражения сборок.Type: представляет тип, включая категории, структуры, интерфейсы и т. д., и предоставляет функцию получения информации о типе.MethodInfo: представляет информацию о методе, позволяя разработчикам проверять свойства метода и вызывать их динамически.PropertyInfo: представляет информацию об атрибутах и обеспечивает доступ к метаданным атрибутов.FieldInfo: представляет информацию о поле и может использоваться для доступа к полям определенного типа.Ниже приводится использованиеSystem.ReflectionПример программы, показывающий, как динамически проверять типы и методы, а также вызывать методы.
//Определяем простой пример класса
общественный класс SampleClass {
публичная строка SayHello (имя строки) {
return $"Привет, {имя}!";
}
}
// Используем Reflection для динамического вызова методов
использование системы;
использование System.Reflection;
класс Программа {
статическая пустота Main() {
//Создаем экземпляр класса SampleClass
Введите sampleType = typeof(SampleClass);
объект sampleInstance = Activator.CreateInstance(sampleType);
// Получаем информацию о методе SayHello
MethodInfo MethodInfo = sampleType.GetMethod("SayHello");
// Динамический вызов метода SayHello
результат объекта = методInfo.Invoke(sampleInstance, новый объект [] { "Мир" });
Console.WriteLine(результат); // Вывод: Привет, мир!
}
}
В приведенном выше примере мы используемActivator.CreateInstanceчтобы создать экземпляр категории и использоватьMethodInfo.Invokeвызвать методSayHello。
System::Management::ManagementClassЭто один из классов .NET Framework, обеспечивающий работу с инструментарием управления Windows (WMI). Он позволяет разработчикам читать, манипулировать и управлять системной информацией, такой как оборудование, операционная система, настройки сети и т. д.
System::ManagementнаходитсяSystem.Management.dllПрежде чем использовать эту функцию, вам необходимо добавить ссылку в пространство имен.
// Метод написания C++/CLI
использование пространства имен System;
используя пространство имен System::Management;
интервал основной() {
попробуй {
ManagementClass^ mc = gcnew ManagementClass("Win32_OperatingSystem");
для каждого (ManagementObject^ mo in mc->GetInstances()) {
Console::WriteLine("Имя ОС: {0}", mo["Caption"]);
}
}
поймать (Исключение^ ex) {
Console::WriteLine("Ошибка: {0}", ex->Message);
}
вернуть 0;
}
System.Management.dllссылка.ManagementException。ManagementObjectManagementObjectSearcherManagementBaseObjectnet stop winmgmt winmgmt /resetrepository net start winmgmt
Get-WmiObject Win32_NetworkAdapterConfigurationСпособ 2. Инструмент тестирования WMI (wbemtest).
wbemtestroot\cimv2и подключитьSELECT * FROM Win32_NetworkAdapterConfigurationDISM /Online /Cleanup-Image /RestoreHealthВосстановить образ системыВ проектах .NET C++ (или, чаще, C++/CLI) чтение и анализ HTML-таблиц в файлах обычно требует использования внешней библиотеки анализа HTML, поскольку стандартная платформа .NET и сам C++ не имеют встроенных мощных возможностей анализа HTML DOM. Распространенный и эффективный вариант — использовать библиотеку на C# или другом языке .NET, а затем ссылаться на нее в C++/CLI.
Для проектов C++/CLI наиболее практичным и рекомендуемым подходом является использованиеHtml Agility Pack(HAP), очень популярный анализатор HTML .NET. Хотя HAP написан на C#, его можно легко использовать в качестве справочника на любом языке .NET, включая C++/CLI.
HtmlAgilityPack。Ниже приведен фрагмент кода C++/CLI, демонстрирующий, как читать локальный файл HTML и анализировать первую таблицу в нем (<table>) содержание:
#using <System.dll>
#using <System.Xml.dll>
#using <HtmlAgilityPack.dll> // Убедитесь, что ссылка включена
использование пространства имен System;
используя пространство имен System::IO;
использование пространства имен HtmlAgilityPack;
void ParseHtmlTable (String^ filePath)
{
// Проверяем, существует ли файл
если (!File::Exists(filePath))
{
Console::WriteLine("Ошибка: файл не существует.");
возвращаться;
}
// 1. Загрузите HTML-файл
HtmlDocument^ doc = gcnew HtmlDocument();
попробуй
{
//Загружаем HTML из файла
документ-> Загрузить (путь к файлу);
}
поймать (Исключение^ ex)
{
Console::WriteLine("Произошла ошибка при загрузке файла: " + ex->Message);
возвращаться;
}
// 2. Выберите первую <table> узел
HtmlNode^ table = doc->DocumentNode->SelectSingleNode("//table");
если (таблица!= nullptr)
{
Console::WriteLine("Найдите таблицу HTML и начните анализ...");
// 3. Выбрать все <tr> (столбец таблицы) узлы
HtmlNodeCollection^ rows = table->SelectNodes(".//tr");
если (строки!= nullptr)
{
для каждой (строки HtmlNode^ в строках)
{
// 4. Выберите <td> или <th> (ячейка) узел
// Используйте | оператор для выбора <td> или <th>
HtmlNodeCollection^ cell = row->SelectNodes("td | th");
если (ячейки!= nullptr)
{
Строка^ rowData = "";
для каждой (ячейки HtmlNode^ в ячейках)
{
// Получаем внутренний текст ячейки и удаляем начальные и конечные пробелы
rowData += cell->InnerText->Trim() + "\t";
}
Консоль::WriteLine(rowData);
}
}
}
еще
{
Console::WriteLine("Тег<tr> не найден в таблице.");
}
}
еще
{
Console::WriteLine("Тег<table> не найден в файле.");
}
}
int main(array<String^>^ args)
{
// Пожалуйста, замените "your_html_file.html" фактическим путем к файлу.
String^ htmlFilePath = "C:\\путь\\к\\ваш_html_файл.html";
ParseHtmlTable(htmlFilePath);
вернуть 0;
}
doc->Load(filePath): чтение локальных файлов HTML в структуру DOM HAP.SelectSingleNode("//table"): используйте выражение XPath, чтобы выбрать первое в файле.<table>элемент.SelectNodes(".//tr"): выберите текущий узел (<table>) все под<tr>элемент.SelectNodes("td | th"):Выбирать<tr>Все под узлом<td>или<th>элемент.cell->InnerText: Получите простой текст содержимого ячейки, и теги HTML будут автоматически удалены.gcnewи^:Это синтаксис, используемый C++/CLI для создания объектов .NET и управления ими (управляемых механизмом сборки мусора CLR).HttpListenerКласс позволяет создать простой автономный HTTP-сервер в приложении C++/CLI.
Создайте проект C++/CLI в Visual Studio (например, консольное приложение CLR) и убедитесь, что на него есть ссылка.System.dll。
Используется следующееHttpListenerКод C++/CLI для создания простого сервера API и обработки запросов GET.
#using <System.dll>
использование пространства имен System;
используя пространство имен System::Net;
используя пространство имен System::Threading::Tasks;
используя пространство имен System::Text;
// Функция для обработки входящих HTTP-запросов
void HandleRequest (контекст HttpListenerContext^)
{
HttpListenerRequest^ запрос = контекст->Запрос;
HttpListenerResponse^ ответ = контекст->Ответ;
//Настройки ответа по умолчанию
ответ->ContentType = "application/json; charset=utf-8";
String^ responseString = "";
ответ->Код состояния = 200; // По умолчанию — ОК
// Проверяем запрошенный путь и метод
String^ URL = request->RawUrl->ToLower();
if (url == "/api/status" && request->HttpMethod == "GET")
{
// Обработка запроса GET /api/status
responseString = "{\"status\": \"Сервер работает\", \"language\": \"C++/CLI\"}";
}
еще
{
// Обработка других неопределенных запросов
ответ->Код состояния = 404; // Не найдено
responseString = "{\"ошибка\": \"404 не найден\", \"путь\": \"" + URL + "\"}";
}
// Преобразуем строковый ответ в байты и записываем в выходной поток
array<Byte>^ buffer = Кодировка::UTF8->GetBytes(responseString);
ответ->ContentLength64 = буфер->Длина;
попробуй
{
ответ->OutputStream->Write(буфер, 0, буфер->Длина);
ответ->Выходной поток->Закрыть();
}
поймать (Исключение^)
{
// Игнорируем ошибки записи, обычно клиент отключается
}
}
// Функция для запуска HttpListener
void StartListener (префикс String^)
{
HttpListener^ прослушиватель = gcnew HttpListener();
попробуй
{
прослушиватель->Префиксы->Добавить(префикс);
слушатель-> Старт();
Console::WriteLine("Сервер C++/CLI API запущен, прослушивает: {0}", префикс);
}
поймать (Исключение^ ex)
{
Console::WriteLine("Произошла ошибка при запуске HttpListener. Пожалуйста, подтвердите, занято ли разрешение или адрес.");
Console::WriteLine("Сообщение об ошибке: {0}", ex->Message);
возвращаться;
}
// Асинхронный цикл, продолжаем получать запросы
в то время как (слушатель->IsListening)
{
попробуй
{
// Синхронно ждем следующего запроса
HttpListenerContext^ context = прослушиватель->GetContext();
// Используйте Task для асинхронной обработки запросов, чтобы избежать блокировки циклов прослушивания
Task::Factory->StartNew(gcnew Action<HttpListenerContext^>(&HandleRequest), context);
}
поймать (Исключение^)
{
// Исключение будет выброшено, когда прослушиватель остановлен
перерыв;
}
}
если (слушатель->IsListening)
{
слушатель->Закрыть();
}
}
int main(array<String^>^ args)
{
//Устанавливаем префикс URL для мониторинга
// Примечание. В Windows могут потребоваться права администратора или использование netsh для регистрации пространства имен URL.
String^ ListenPrefix = "http://localhost:8080/";
StartListener (префикс прослушивания);
Console::WriteLine("Нажмите любую клавишу, чтобы завершить работу сервера...");
Консоль::ReadKey(истина);
вернуть 0;
}
В системах Windows, если код запускается без прав администратора,HttpListenerИсключение отказа в доступе может быть выдано при прослушивании определенного порта. Вы можете зарегистрировать пространство имен URL-адресов, чтобы разрешить учетным записям, не являющимся администраторами, запускать сервер с помощью следующей команды:
netsh http add urlacl url=http://+:8080/ user=Everyone
вhttp://+:8080/следует заменить фактическим префиксом, используемым в вашем коде.
В ASP.NET Core стандартным типом проекта для создания сервера HTTP API является веб-API. Рекомендуется последняя версия .NET (.NET 6 и выше).Minimal APIsдля быстрого и легкого создания конечных точек API.
Минимальные API объединяют логику запуска (Запуск) и определения маршрутизации (Маршрутизация) в один файл с использованием минимального количества файлов и строк кода.Program.csсередина.
Чтобы создать новый проект минимального API, вы можете использовать .NET CLI (интерфейс командной строки):
dotnet new web -n MinimalApiServer
cd MinimalApiServer
Ниже приведен основной файл сервера Minimal API.Program.csполное содержание. Он определяет запуск сервера, промежуточное программное обеспечение и две конечные точки API (одну GET, одну POST).
// 1. Создайте экземпляр веб-приложения (заменив традиционную категорию «Автозагрузка»)
вар строитель = WebApplication.CreateBuilder(args);
// 2. Регистрация службы (Конфигурация службы)
//Здесь можно добавить подключение к базе данных, сервисы проверки и т.д.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); // Включаем поддержку Swagger/OpenAPI
вар приложение = builder.Build();
// 3. Настройка конвейера HTTP-запросов (конфигурация промежуточного программного обеспечения)
// В среде разработки включаем Swagger UI для тестирования
если (app.Environment.IsDevelopment())
{
приложение.UseSwagger();
app.UseSwaggerUI();
}
// Включить перенаправление HTTPS (рекомендуется для производственных сред)
// app.UseHttpsRedirection();
// 4. Определить конечную точку API (Определение конечной точки)
// Класс сущности, используемый для POST-запросов
общедоступная запись Product (int Id, строковое имя, десятичная цена);
// Конечная точка запроса GET: /api/hello
app.MapGet("/api/hello", () =>
{
returnResults.Ok(new { message = "Привет от ASP.NET Core Minimal API!", timestamp = DateTime.Now });
});
// Конечная точка запроса GET: /api/products/{id}
app.MapGet("/api/products/{id}", (int id) =>
{
// Предположим, запрашиваем продукты из базы данных
если (идентификатор == 1)
{
returnResults.Ok(новый продукт(1, "ноутбук", 1200,00 м));
}
returnResults.NotFound(new { error = $"Идентификатор продукта {id} не найден" });
});
// Конечная точка POST-запроса: /api/products
app.MapPost("/api/products", (Продукт) =>
{
// ASP.NET Core автоматически десериализует тело запроса JSON в сущность Product
Console.WriteLine($"Получен новый товар: {product.Name} (ID: {product.Id})");
//В реальном приложении здесь будет выполняться операция добавления базы данных.
//Вернем 201 Код статуса создан
returnResults.Created($"/api/products/{product.Id}", product);
});
// 5. Запускаем приложение и начинаем прослушивать HTTP-запросы
приложение.Выполнить();
dotnet run。http://localhost:5000илиhttps://localhost:7000бегать./api/hello (GET)。/swaggerПуть для просмотра пользовательского интерфейса Swagger для тестирования всех определенных конечных точек API.app.UseSwagger()、app.UseHttpsRedirection()И т. д., эти компоненты последовательно составляют конвейер обработки запросов. Каждый запрос проходит через это промежуточное программное обеспечение.app.MapPostВ этом примере, когда клиент отправляет данные JSON, ASP.NET Core автоматически преобразует (десериализует) их в C#.ProductЗаписывать.Большинство современных Chromebook поддерживают запуск приложений Linux через проект Crostini.
Настройки > Дополнительно > Разработчик > Включить Linux (бета)。sudo apt update
sudo apt install -y dotnet-sdk-7.0
Вы можете использовать облачную платформу для запуска программ .NET на Chromebook без установки какого-либо программного обеспечения локально.
Chromebook поддерживает использование контейнеров для запуска приложений, и вы можете запустить среду .NET через Docker.
docker pull mcr.microsoft.com/dotnet/runtime:7.0
Начиная с .NET 6, приложения могут работать на нескольких платформах, включая Linux. Скомпилируйте свое приложение в кроссплатформенный формат и разверните его на Chromebook.
dotnet publish -c Release -r linux-x64 --self-contained
использование системы;
использование System.Windows.Forms;
открытый класс MyForm: Форма {
публичная MyForm() {
Кнопка btn = новая кнопка();
btn.Text = "Нажмите на меня";
btn.Click += (s, e) => MessageBox.Show("Вы нажали кнопку!");
Controls.Add(BTN);
}
[STAThread]
статическая пустота Main() {
Приложение.EnableVisualStyles();
Application.Run(новый MyForm());
}
}
// MainWindow.xaml
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="Пример WPF" Height="200" Width="300">
<StackPanel>
<Button Content="Нажмите на меня" Click="Button_Click"/>
</StackPanel>
</Окно>
// MainWindow.xaml.cs
частный void Button_Click (отправитель объекта, RoutedEventArgs e) {
MessageBox.Show("Вы нажали кнопку!");
}
// Главная страница.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
x:Class="MyApp.MainPage">
<VerticalStackLayout Spacing="25" Padding="30">
<Button Text="Нажмите на меня" Clicked="OnButtonClicked"/>
</VerticalStackLayout>
</ContentPage>
// Главная страница.xaml.cs
Private void OnButtonClicked (отправитель объекта, EventArgs e) {
await DisplayAlert("Уведомление", "Вы нажали кнопку!", "ОК");
}
| технология | использовать | платформа |
|---|---|---|
| WinForms | Быстрая разработка настольных приложений | Windows |
| WPF | Сложные настольные приложения, шаблон MVVM | Windows |
| MAUI | Кроссплатформенное приложение | Windows、macOS、Android、iOS |
| Blazor | Веб-интерфейсная разработка | Кроссплатформенный (Интернет) |
Следующие примеры показывают, как использоватьControls->GetEnumerator()Метод для перебора всех дочерних элементов управления в форме .NET один за другим и пакетного изменения их свойств.
использование системы;
использование System.Drawing;
использование System.Windows.Forms;
общедоступный класс FormExample: Форма
{
публичный пример формы()
{
//Инициализируем некоторые элементы управления
Кнопка button1 = новая кнопка { Text = «Кнопка 1», Location = новая точка (10, 10) };
TextBox textBox1 = новый TextBox {Местоположение = новая точка (10, 50)};
Controls.Add(button1);
Controls.Add(textBox1);
// Используйте GetEnumerator() для перемещения и изменения свойств элемента управления
МодифицироватьЭлементы управления();
}
частная пустота ModifyControls()
{
вар перечислитель = Controls.GetEnumerator();
в то время как (перечислитель.MoveNext())
{
Управление control = (Control)enumerator.Current;
// Пример настройки: установить голубой цвет фона всех элементов управления
control.BackColor = Color.LightBlue;
// Если элементом управления является TextBox, сделайте его недоступным для редактирования
if (элемент управления — TextBox)
{
control.Enabled = ложь;
}
}
}
//Запускаем приложение
общественная статическая пустота Main()
{
Application.Run(новый FormExample());
}
}
**GetEnumerator()**: используйте метод Controls.GetEnumerator(), чтобы получить перечислитель элемента управления, чтобы можно было просмотреть все дочерние элементы управления.
**Условное изменение**. В цикле while измените свойства каждого объекта Control, например, установив светло-голубой цвет фона, и внесите определенные изменения в зависимости от типа элемента управления, например, установив для TextBox значение нередактируемого.
**Использование**. Этот метод очень эффективен, когда вам нужно изменить свойства в пакетном режиме, например настроить стиль пользовательского интерфейса или отключить несколько элементов управления при определенных обстоятельствах.
После выполнения цвет фона всех элементов управления изменится на светло-синий, а все элементы управления TextBox станут недоступными для редактирования.
В С++/CLIMessageBox::ShowЭто статический метод, используемый для открытия диалогового окна. Самый полный метод вызова включает в себя: содержание сообщения, заголовок, тип кнопки, значок и кнопку по умолчанию.
используя пространство имен System::Windows::Forms;
//Простейшее отображение
MessageBox::Show("Операция завершена");
//Полная версия параметра
Результат DialogResult = MessageBox::Show(
«Вы уверены, что хотите удалить этот файл?», // Содержимое сообщения (Текст)
"Подтвердить удаление", // заголовок окна (Caption)
MessageBoxButtons::ДаНет, // Комбинация кнопок
MessageBoxIcon::Warning, // Значок подсказки
MessageBoxDefaultButton::Button2 // Фокус по умолчанию находится на второй кнопке (Нет)
);
Необходимо установить флажок, если кнопка содержит несколько вариантов, например «Да/Нет» или «ОК/Отмена».DialogResultчтобы определить последующую логику.
if (результат == DialogResult::Да) {
//Выполняем логику удаления
} еще {
//Отменяем операцию
}
| категория | Общие варианты | иллюстрировать |
|---|---|---|
| MessageBoxButtons | OK, OKCancel, YesNo, YesNoCancel | Определяет, какие кнопки отображаются под диалоговым окном. |
| MessageBoxIcon | Information, Warning, Error, Question | Определите значок и системные звуковые эффекты, отображаемые слева. |
| DialogResult | OK, Cancel, Yes, No | ShowВозвращаемое значение метода показывает, какую клавишу нажал пользователь. |
потому чтоMessageBox::ShowПолучено .NETSystem::String^, если у вас есть собственные данные C++, вам необходимо их преобразовать.
#include <строка>
#include <msclr/marshal_cppstd.h>
// Отображение std::string в MessageBox
std::string cpp_str = "Ошибка ядра";
MessageBox::Show(msclr::interop::marshal_as<System::String^>(cpp_str));
//Форматируем число с плавающей запятой и отображаем его
плавающее расстояние = 1,23456f;
MessageBox::Show("Расстояние: " + dist.ToString("F2")); // Отображение «Расстояние: 1,23»
MessageBoxButtons::OK。using namespace System;иusing namespace System::Windows::Forms;, возможно, с другимиMessageBoxКонфликт определений, рекомендуется использовать полное имя.System::Windows::Forms::MessageBox::Show(...)。System::Windows::Forms::MessageBoxпредназначен для отображения «уведомлений» или «предупреждений» и получения решений пользователя с помощью стандартных кнопок. Он не имеет функции динамической вставки элементов управления. Если вам нужно ввести текст, стандартным подходом является наследованиеSystem::Windows::Forms::FormСоздайте собственное диалоговое окно.
Это простая категория многократного использования, содержащая метки, текстовые поля и кнопки ОК.
использование пространства имен System;
используя пространство имен System::Windows::Forms;
используя пространство имен System::Drawing;
публичный класс ссылки CustomInputBox: общественная форма {
публика:
Текстовое поле^ txtInput;
Кнопка^ btnOK;
Метка^ lblPrompt;
CustomInputBox (заголовок String^, приглашение String^) {
//Установим основные свойства формы
это->Текст = заголовок;
это->Размер = Система::Рисование::Размер(300, 150);
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog;
это->StartPosition = FormStartPosition::CenterParent;
это->MaximizeBox = ложь;
это->MinimizeBox = ложь;
// Текст подсказки
lblPrompt = gcnew Label();
lblPrompt->Текст = подсказка;
lblPrompt->Местоположение = Точка(15, 15);
lblPrompt->Size = System::Drawing::Size(250, 20);
// поле ввода
txtInput = gcnew TextBox();
txtInput->Location = Point(15, 40);
txtInput->Size = System::Drawing::Size(250, 20);
// кнопка ОК
btnOK = gcnew Button();
btnOK->Текст = «ОК»;
btnOK->DialogResult = System::Windows::Forms::DialogResult::OK;
btnOK->Местоположение = Точка(190, 75);
// Добавляем элементы управления
это->Управление->Добавить(lblPrompt);
это->Управление->Добавить(txtInput);
это->Управление->Добавить(btnOK);
//Установим кнопку по умолчанию (нажатие Enter эквивалентно нажатию OK)
это->AcceptButton = btnOK;
}
};
использоватьShowDialog()Метод открывает окно в режиме принудительного ответа и проверяет, является ли возвращаемое значениеOK。
недействительный OnButtonClick () {
CustomInputBox^ ibox = gcnew CustomInputBox("Системный ввод", "Введите имя файла:");
if (ibox->ShowDialog() == System::Windows::Forms::DialogResult::OK) {
String^ userInput = ibox->txtInput->Text;
if (!String::IsNullOrWhiteSpace(userInput)) {
// Обрабатываем строку, введенную пользователем
MessageBox::Show("Вы ввели: " + userInput);
}
}
}
| план | Сложность реализации | преимущество | недостаток |
|---|---|---|---|
| Пользовательская форма | середина | Полный контроль над макетом и возможность добавлять логику проверки (например, ограниченное количество). | Необходимо написать больше кода интерфейса. |
| VB Interaction | Низкий | Одна строка кода завершена. | Нужно процитироватьMicrosoft.VisualBasic, стиль фиксирован и не может быть изменен. |
| Win32 API | высокий | Среда .NET не требуется. | Разработка на C++/CLI чрезвычайно тривиальна, и ее сложно поддерживать. |
delete ibox;Или убедитесь, что он правильно освобождает ресурсы после завершения области действия.ShowDialog()скорее, чемShow(), это гарантирует, что пользователь должен закрыть окно ввода перед работой с главным окном и сможет напрямую получитьDialogResult。PictureBox отображает красный крестик, указывающий, чтоOnPaintВо время выполнения произошло исключение,
Однако .NET по умолчанию поглощает это исключение и не отображает его в общем журнале или точках прерывания, что затрудняет отладку.
| причина | иллюстрировать |
|---|---|
| Растровое изображение было удалено | PictureBox по-прежнему содержит ссылку на растровое изображение, а доступ к освобожденным ресурсам осуществляется во время Paint. |
| Исходный поток закрыт | После использования Stream для создания растрового изображения и последующего закрытия потока данные растрового изображения станут недействительными. |
| Межпоточный доступ | Фоновый поток напрямую изменяет или назначает Bitmap, что конфликтует с потоком пользовательского интерфейса. |
| Исчерпание ресурсов GDI+ | Большое количество растровых изображений не удаляется, что приводит к нехватке памяти GDI+. |
| Сообщение об исключении | первопричина | Направление решения |
|---|---|---|
| ArgumentException: Parameter is not valid | Растровое изображение было удалено или поток был закрыт. | Глубокая копия Bitmap, не зависит от Stream |
| InvalidOperationException: object is in use elsewhere | Доступ к одному и тому же растровому изображению одновременно в разных потоках | Добавьте блокировку или вернитесь к потоку пользовательского интерфейса. |
| OutOfMemoryException | Ресурсы GDI+ исчерпаны или формат изображения не поддерживается. | Немедленно утилизируйте старое растровое изображение и проверьте источник изображения. |
| ExternalException: A generic error occurred in GDI+ | Файл или поток, соответствующий растровому изображению, больше не существует. | Убедитесь, что источник по-прежнему действителен в течение срока службы растрового изображения. |
Поскольку проблема с красным крестом может возникать не часто, рекомендуется действовать по порядку:
OnPaint,
Используйте try/catch для перехвата исключений и вывода их вDebug.WriteLine,
выполнить после захватаthis.Image = nullИзбегайте постоянных красных крестов.Environment.StackTrace, узнать, кто и в какой момент времени выпустил ресурс.ImageУстановите значение null, а затем Dispose.Все операции по смене изображений выполняются в этом режиме, чтобы обеспечить потокобезопасность и правильный порядок Dispose:
// С++/CLI
void SafeUpdateImage(Bitmap^ newBmp) {
если (pictureBox1->InvokeRequired) {
PictureBox1->Invoke(gcnew Action<Bitmap^>(
это, &YourClass::SafeUpdateImage), newBmp);
возвращаться;
}
Bitmap^ old = Safe_cast<Bitmap^>(pictureBox1->Image);
PictureBox1-> Изображение = newBmp; // Сначала переходим к новому изображению
if (old != nullptr) old->Dispose(); // Снова утилизируем старую картинку
}
// Ошибка: растровое изображение недействительно после закрытия потока
используя (var поток = новый FileStream (путь, FileMode.Open)) {
PictureBox1.Image = новое растровое изображение (поток);
}
// Правильно: глубокое копирование, не полагается на поток
Растровое изображение загружено;
используя (varstream = новый FileStream(path, FileMode.Open))
используя (var tmp = новое растровое изображение (поток)) {
загружено = новое растровое изображение (tmp);
}
PictureBox1.Image = загружено;
| Проверить элементы | Способ подтверждения |
|---|---|
| Размещено ли растровое изображение в другом месте | Унифицированно меняйте изображения с помощью SafeUpdateImage и записывайте StackTrace. |
| Закрыт ли исходный поток | Вместо этого используйте глубокое копированиеnew Bitmap(tmp) |
| Следует ли осуществлять доступ через потоки | Проверьте InvokeRequired при изменении изображений, всегда вызывайте обратно в поток выполнения пользовательского интерфейса. |
| Красный крест появляется сразу или через некоторое время? | Если оно появляется сразу, это может быть источником проблемы; через некоторое время это может быть проблема с синхронизацией Dispose. |
Когда в трассировке стека появляется следующее сообщение об ошибке, разработчикам может потребоваться проверитьMessageСодержимое объекта:
at ....MainForm.Dispose(Boolean A_0)
at System.Windows.Forms.Form.WmClose(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
...
В это время необходимо перезаписать или обнаружитьWndProcв методеMessage mчтобы проверить его детали.
Вот несколько способов проверить или записатьMessageСодержимое объекта.
Если у вас есть доступ к исходному коду формы или элемента управления, рекомендуется перезаписатьWndProcметод, прямая запись или проверкаMessage mсодержание.
защищенное переопределение void WndProc (ref Message m)
{
попробуй
{
//Запись содержимого сообщения
Console.WriteLine($"Сведения о сообщении: hWnd={m.HWnd}, Msg={m.Msg}, WParam={m.WParam}, LParam={m.LParam}");
base.WndProc(ref m);
}
поймать (исключение ex)
{
Console.WriteLine($"Исключение: {ex}");
бросать; // Сохраняем исходное поведение исключения
}
}
Этот код будет регистрировать соответствующую информацию каждый раз при получении сообщения и позволит разработчику проанализировать ее дальше.
Если ошибка возникает вDisposeВ методе вы можете добавить обработку исключений внутри метода для проверки соответствующей информации.
защищенное переопределение void Dispose (удаление bool)
{
попробуй
{
если (удаление)
{
// Освобождаем ресурсы
}
base.Dispose(удаление);
}
поймать (исключение ex)
{
Console.WriteLine($"Исключение в Dispose: {ex}");
бросать;
}
}
Это гарантирует, что важная информация об ошибках не будет проигнорирована при освобождении ресурсов.
Если вы не можете определить, где произошла ошибка, вы можете использовать глобальную обработку исключений для записи трассировок стека и связанной информации.
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
Exception ex = (Exception)args.ExceptionObject;
Console.WriteLine($"Unhandled Exception: {ex}");
Environment.Exit(1);
};
Вы можете использовать Visual Studio для установки точек останова вWndProcилиDisposeметод и проверкаMessageСодержимое объекта.
HWnd: Дескриптор окна, которое получает сообщение.Msg: идентификатор сообщения.WParam: Дополнительная информация сообщения (параметр слова).LParam: Дополнительная информация сообщения (длинный параметр).WndProcИли будьте осторожны при изменении кода, чтобы не повлиять на существующее поведение.MessageКоординаты или идентификатор не фиксированы, вы можете добавить условное суждение для фильтрации необходимой информации.В .NET C++/CLIWmClose(Message& m)даSystem.Windows.Forms.FormМетод внутренней защиты не может быть перезаписан напрямую. Однако можно переопределитьWndProcметод перехвата и обработкиWM_CLOSEсообщение для достижения аналогичного переопределенияWmCloseэффект.
#include <Windows.h>
#include <System.Windows.Forms.h>
использование пространства имен System;
используя пространство имен System::Windows::Forms;
общедоступный класс ссылки CustomForm: общедоступная форма
{
защищено:
// Имитация переопределения поведения WmClose
void WmClose(Сообщение% м)
{
//Добавляем сюда собственное поведение WM_CLOSE
if (MessageBox::Show("Вы уверены, что хотите закрыть окно?", "Подтвердить", MessageBoxButtons::ДаНет) == DialogResult::Да)
{
// Продолжаем вызывать базовое поведение для корректного завершения работы
this->Form::WndProc(m);
}
еще
{
// Предотвращаем закрытие окна
возврат;
}
}
// Переопределить WndProc, перехватить сообщение WM_CLOSE и вызвать WmClose
переопределение виртуальной пустоты WndProc (Message% m)
{
const int WM_CLOSE = 0x0010;
если (m.Msg == WM_CLOSE)
{
WmClose(м); // Вызов пользовательского метода WmClose
}
еще
{
// Обрабатываем другие сообщения
Форма::WndProc(м);
}
}
};
[STAThread]
int main(array<String^>^ args)
{
Приложение::EnableVisualStyles();
Приложение::SetCompatibleTextRenderingDefault(false);
CustomForm^ form = gcnew CustomForm();
form->Text = "Пример переопределения поведения WmClose";
Приложение::Выполнить(форма);
вернуть 0;
}
имитировать перезаписьWmCloseЗдесь можно настроить поведение закрытия окна. Используйте диалоговое окно подтверждения, чтобы спросить пользователя, хотят ли они закрыть окно.
перезаписатьWndProcметод перехватаWM_CLOSEсообщение и делегировать его пользовательскомуWmCloseметод.
без перехватаWM_CLOSEВ случае вызова базового классаWndProcМетод обработки других сообщений.
WmCloseМетод лишь имитирует перезапись, но на самом деле обрабатывает сообщение, перехватывая его.Form::WndProcчтобы убедиться, что базовое поведение работает правильно.InvokeRequiredдаSystem::Windows::Forms::ControlАтрибут используется для определения того, является ли текущий поток выполнения потоком выполнения пользовательского интерфейса, которому принадлежит элемент управления. При доступе к элементам управления пользовательского интерфейса из фонового потока вы должны использоватьInvokeМаршалируйте операцию обратно в поток пользовательского интерфейса.использование пространства имен System;
используя пространство имен System::Windows::Forms;
используя пространство имен System::Drawing;
ссылка на класс ImageHelper {
публика:
static void SetImageSafe(PictureBox^ pPictureBox, Bitmap^ b) {
если (pPictureBox == nullptr)
возврат;
если (pPictureBox->InvokeRequired) {
// Используем MethodInvoker для вызова той же функции, но выполняем ее в потоке пользовательского интерфейса
pPictureBox->Вызов(
gcnew MethodInvoker (gcnew Action
InvokeRequiredМожно использовать только для наследования.ControlОбъект (например, PictureBox).true, что означает, что текущий поток выполнения не является потоком выполнения пользовательского интерфейса и его необходимо использоватьInvoke。TupleИнкапсулируйте несколько параметров и передайте их в статическую прокси-функцию для обработки.try-catchПерехват исключений позволяет избежать сбоев программы.
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Drawing;
void SetImageSafe(PictureBox^ pPictureBox, Bitmap^ b) {
if (pPictureBox->InvokeRequired) {
pPictureBox->Invoke(gcnew MethodInvoker(
gcnew EventHandler(nullptr, &SetImageInvoker)
), gcnew array
использование пространства имен System;
используя пространство имен System::Windows::Forms;
используя пространство имен System::Drawing;
void SafeAssignImage(PictureBox^ pPictureBox, Bitmap^ b) {
если (pPictureBox->InvokeRequired) {
pPictureBox->Invoke(gcnew MethodInvoker(gcnew делегат {
попробуй {
если (b != nullptr) {
если (pPictureBox->Image!= nullptr)
удалить pPictureBox->Изображение;
pPictureBox-> Изображение = б;
}
} catch (System::Exception^ ex) {
Console::WriteLine("Не удалось установить изображение: " + ex->Message);
}
}));
} еще {
попробуй {
если (b != nullptr) {
если (pPictureBox->Image!= nullptr)
удалить pPictureBox->Изображение;
pPictureBox-> Изображение = б;
}
} catch (System::Exception^ ex) {
Console::WriteLine("Не удалось установить изображение: " + ex->Message);
}
}
}
gcnew MethodInvoker(...)Должно быть допустимое делегирование. Лямбда-синтаксис C++11 (например, [=]()) не поддерживается.BeginInvokeсоответствоватьObject^Массив и анализ.Blazor — это интерфейсная платформа, запущенная Microsoft, которая позволяет разработчикам использовать синтаксис C# и Razor для создания интерактивных веб-приложений. Код C# можно выполнить в браузере без использования JavaScript.
Blazor легко интегрирует ASP.NET Core, может комбинироваться с существующими приложениями .NET и поддерживает внедрение зависимостей, маршрутизацию, архитектуру компонентов и другие функции.
Для разработчиков, основной технологией которых является .NET, Blazor предоставляет решение, соответствующее унифицированному языку JavaScript Node.js, позволяющее как клиентской, так и внутренней части использовать C#, создавая более последовательный опыт разработки.
.NET MAUI (UI многоплатформенного приложения) — это кроссплатформенная платформа приложений, выпущенная Microsoft. Вы можете использовать C# и XAML для единоразового написания кода и его развертывания в Windows, Android, iOS и даже macOS.
Гибридный режим Blazor в .NET MAUI можно использовать для встраивания веб-интерфейса в собственные приложения, что позволяет разработчикам использовать компоненты Razor для разработки кроссплатформенных пользовательских интерфейсов, сохраняя при этом доступ к собственным API.
.NET MAUI на данный момент является наиболее полным кроссплатформенным решением .NET. Его можно объединить с Blazor для достижения унифицированной разработки приложений для веб-платформ, настольных и мобильных платформ, предоставляя разработчикам .NET полноценный опыт, согласованный по языку и технологиям.
.NET MAUI можно интегрировать с серверными службами, созданными ASP.NET Core, посредством вызовов API, чтобы сформировать полную архитектуру «внешнее приложение + внутренний API».
ASP.NET Core решает проблемы «внутренних служб» и «веб-приложений», а .NET MAUI фокусируется на «кроссплатформенных приложениях на стороне клиента». Эти два понятия не являются взаимоисключающими, но часто используются вместе для формирования комплексного решения.
Ниже приведен пример кросс-платформенного приложения, созданного с использованием .NET MAUI, которое отображает на экране кнопку, обновляющую счетчик текста при нажатии.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp.MainPage">
<VerticalStackLayout Spacing="25" Padding="30">
<Label x:Name="counterLabel"
Text="Вы еще не нажали кнопку"
Размер шрифта="24"
HorizontalOptions="Центр" />
<Текст кнопки="Нажмите на меня"
Clicked="OnCounterClicked"
HorizontalOptions="Центр" />
</VerticalStackLayout>
</ContentPage>
пространство именMauiApp;
общедоступный частичный класс MainPage: ContentPage
{
число интервалов = 0;
публичная главная страница()
{
ИнициализироватьКомпонент();
}
частная пустота OnCounterClicked (отправитель объекта, EventArgs e)
{
считать++;
counterLabel.Text = $"Вы нажали {count} раз";
}
}
Этот простой пример демонстрирует кроссплатформенные возможности .NET MAUI. Разработчикам достаточно написать код XAML и C# только один раз, чтобы запустить его на нескольких платформах одновременно.
MainPage.xaml: Определить интерфейс пользовательского интерфейса (с использованием XAML).MainPage.xaml.cs: Внутренний код (C#) обрабатывает события.MauiProgram.cs: точка запуска приложения и регистрации службы.PlatformsПапки: собственные настройки для каждой платформы (Android, iOS, Windows, MacCatalyst).Для создания проекта .NET MAUI с помощью Visual Studio требуется всего несколько шагов, а один набор кода можно развернуть на нескольких платформах одновременно, что делает его идеальным выбором для современных полноценных разработчиков C#.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyMauiApp.MainPage">
<VerticalStackLayout Spacing="25" Padding="30"
ВертикальныеОпции="Центр">
<Ярлык
Text="Добро пожаловать в .NET MAUI!"
SemanticProperties.HeadingLevel="Level1"
Размер Шрифта="32"
HorizontalOptions="Центр" />
<Кнопка
x:Name="counterBtn"
Text="Нажмите на меня!"
Clicked="OnCounterClicked"
HorizontalOptions="Центр" />
</VerticalStackLayout>
</ContentPage>
использование системы;
пространство имен MyMauiApp;
общедоступный частичный класс MainPage: ContentPage
{
число интервалов = 0;
публичная главная страница()
{
ИнициализироватьКомпонент();
}
частная пустота OnCounterClicked (отправитель объекта, EventArgs e)
{
считать++;
counterBtn.Text = $"Вы нажали {count} раз";
}
}
Spacing(интервал) иPadding(внутреннее расстояние).x:Nameдля доступа через серверную часть C#,ClickedАтрибут определяет метод обработки события щелчка.InitializeComponent(): инициализировать структуру пользовательского интерфейса, определенную в XAML.OnCounterClicked: метод обработки события щелчка, обновляет текст кнопки каждый раз при нажатии кнопки.counterBtn.Text: Управляйте свойствами кнопки через имя, указанное в XAML.После выполнения в центре экрана отобразится приветственное сообщение и кнопка. Каждый раз, когда вы нажимаете кнопку, текст на кнопке обновляется: «Вы нажали X раз».
using Microsoft.Extensions.Logging;
namespace MyMauiApp;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp() //Указываем точку входа приложения
.ConfigureFonts(шрифты =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if ОТЛАДКА
builder.Logging.AddDebug();
#endif
вернуть строитель.Build();
}
}
MauiAppПример.App.xaml.csКатегория приложения), это будет корневой компонент приложения.builder.Services.AddSingleton<MainPage>();Это позволяет загружать MainPage с помощью внедрения зависимостей, например, в классе App.
serviceProvider.GetService<MainPage>()。
builder.Services.AddTransient<MyViewModel>();
builder.Services.AddSingleton<IMyService, MyService>();
MauiProgram.csТочно так же, как ASP.NET CoreStartup.csилиProgram.cs, является основным центром настройки для запуска приложений и регистрации служб. При разработке больших приложений MAUI очень часто этот файл расширяют, добавляя DI, ведение журнала, настройки компонентов и т. д.
Развертывание .NET C++ (приложений C++/CLI или C++ на основе .NET) в системах Ubuntu необходимо выполнять отдельно в зависимости от типа проекта. Если вы используете чистый C++/CLI (полагаясь на .NET Framework), вы не сможете выполнить его непосредственно в Linux, и вам необходимо использоватьКроссплатформенная технология для .NET 6/7/8(Например, C++/CLR → C# или использование C++/Native с совместимостью .NET).
В зависимости от характера приложения развертывание можно разделить на три сценария:
Установите .NET SDK и необходимые инструменты в Ubuntu:
sudo apt update
sudo apt install -y dotnet-sdk-8.0
sudo apt install -y build-essential
(Можно заменить в зависимости от версииdotnet-sdk-8.0дляdotnet-sdk-7.0или другие версии)
Если вы используете .NET C# в качестве основной программы и вызываете библиотеку C++:
# Создаем основное приложение
новая консоль dotnet -n MyApp
cdMyApp
# Создать собственную библиотеку функций
родной mkdir
родной компакт-диск
нано add.cpp
Пример add.cpp:
extern "C" int add(int a, int b) {
return a + b;
}
Скомпилируйте как библиотеку общих функций:
g++ -fPIC -shared -o libadd.so add.cpp
ВProgram.csприсоединяйтесь:
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("libadd.so", EntryPoint="add")]
public static extern int Add(int a, int b);
static void Main()
{
Console.WriteLine("3 + 5 = " + Add(3, 5));
}
}
Вернитесь в корневой каталог проекта и выполните:
dotnet run
Если результат вывода3 + 5 = 8, что указывает на успешное развертывание.
Программы могут быть упакованы в независимые исполняемые файлы развертывания:
dotnet publish -c Release -r linux-x64 --self-contained true
Сгенерированный исполняемый файл находится в папкеbin/Release/net8.0/linux-x64/publish/。
Приложения можно помещать в контейнеры с помощью Docker для запуска в любой системе Ubuntu:
# Пример файла Docker
ОТ mcr.microsoft.com/dotnet/runtime:8.0
РАБОЧИЙ ДИАР/приложение
КОПИРОВАТЬ ./опубликовать .
КОПИРОВАТЬ ./native/libadd.so /usr/lib/
ТОЧКА ВХОДА [./MyApp"]
Сборка и выполнение контейнеров:
docker build -t myapp .
docker run --rm myapp
.soБиблиотека функций, используемая Windows.dll。LD_LIBRARY_PATH。Реальным способом развертывания программ .NET C++ в Ubuntu обычно являетсяC# + собственная библиотека C++". Если исходная программа опирается на C++/CLI, ее необходимо перепроектировать для поддержки кросс-платформенной .NET. Наиболее рекомендуемый способ — использовать .NET 8 + собственные модули C++ с Docker для достижения стабильного, переносимого и согласованного процесса развертывания.
email: [email protected]