Visual Studio 是由微軟開發的一個整合式開發環境(IDE),支援多種程式語言如 C++、C#、VB.NET、Python、JavaScript 等。適用於開發桌面應用程式、網站、雲端服務及行動應用程式。
Visual Studio 支援 Windows 作業系統,另有 Visual Studio for 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 的開始選單搜尋「Developer Command Prompt for VS」開啟。
Ctrl + `
dotnet
命令檢查或操作 SDKusing System;
class Program {
static void Main() {
string name = "世界";
Console.WriteLine($"哈囉, {name}!");
}
}
(x, y) => x + y
var q = list.Where(x => x > 10);
await Task.Delay(1000);
CS8618 意思是:在建構函式結束時,非 Nullable
的屬性沒有被初始化,因此 C# 編譯器警告它可能會是 null
。
public class Product {
public string Name { get; set; } // 警告 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+ 支援)public class Product {
public required string Name { get; set; }
}
// 呼叫端必須初始化
var p = new Product { Name = "手機" }; // OK
null
:用 建構函式 或 required。null
:改成 string?
。string.Empty
)。float
與 System::String
相加,例如:
System::String^ tmpStr = "Value: " + someFloat;
當 someFloat
很大或很小時,可能會自動以科學記號(e.g. 1.23E+05)顯示。
System::String::Format
或 ToString
搭配格式字串,指定小數位數並避免科學記號。
using namespace System;
float value = 123456.789f;
// 方法1:String::Format
String^ tmpStr1 = String::Format("Value: {0:F2}", value); // F2 表示小數點後 2 位
Console::WriteLine(tmpStr1);
// 方法2:ToString 搭配格式
String^ tmpStr2 = "Value: " + value.ToString("F2");
Console::WriteLine(tmpStr2);
// 方法3:更多位數
String^ tmpStr3 = "Value: " + value.ToString("F6");
Console::WriteLine(tmpStr3);
F2
:固定小數點格式,小數點後 2 位F6
:固定小數點格式,小數點後 6 位F{位數}
在 .NET C++ 中,可以使用 System::IO
命名空間提供的功能來操作檔案與目錄。
取得目錄中最新的檔案,可以透過讀取所有檔案資訊並比較最後修改日期實現。
以下是一個完整的範例,展示如何取得指定目錄中最新的檔案:
#include "stdafx.h"
#include <iostream>
#include <cliext/vector>
#include <System.IO>
using namespace System;
using namespace System::IO;
using namespace cliext;
int main()
{
try
{
// 指定目錄路徑
String^ directoryPath = "C:\\Your\\Directory\\Path";
// 檢查目錄是否存在
if (!Directory::Exists(directoryPath))
{
Console::WriteLine("目錄不存在: {0}", directoryPath);
return -1;
}
// 取得目錄中的所有檔案
array^ files = Directory::GetFiles(directoryPath);
// 如果目錄中沒有檔案
if (files->Length == 0)
{
Console::WriteLine("目錄中沒有檔案。");
return 0;
}
// 找到最新的檔案
String^ newestFile = nullptr;
DateTime newestTime = DateTime::MinValue;
for each (String^ file in files)
{
// 取得檔案的最後修改時間
DateTime lastWriteTime = File::GetLastWriteTime(file);
// 比較時間並更新最新檔案資訊
if (lastWriteTime > newestTime)
{
newestTime = lastWriteTime;
newestFile = file;
}
}
// 輸出最新檔案資訊
Console::WriteLine("最新檔案: {0}", newestFile);
Console::WriteLine("最後修改時間: {0}", newestTime);
}
catch (Exception^ ex)
{
Console::WriteLine("發生錯誤: {0}", ex->Message);
}
return 0;
}
Directory::GetFiles
方法獲取指定目錄中的所有檔案路徑。File::GetLastWriteTime
方法取得每個檔案的最後修改時間。try-catch
區塊捕獲潛在的異常,例如目錄不存在或無法存取。System.Reflection
是 .NET 框架中的一個命名空間,提供了檢查和操作元數據的工具,使開發者可以在運行時動態檢查類型、方法、屬性等,並動態創建和操縱對象。
Assembly
:表示已加載的程序集,提供加載、探索和反射程序集的方法。Type
:表示一種類型,包括類別、結構、介面等,提供獲取類型信息的功能。MethodInfo
:表示方法資訊,允許開發者檢查方法的屬性並動態調用它們。PropertyInfo
:表示屬性資訊,提供對屬性元數據的存取。FieldInfo
:表示字段資訊,可用於訪問類型的字段。以下是一個使用 System.Reflection
的範例程式,展示如何動態檢查類型和方法,並調用方法。
// 定義一個簡單的範例類別
public class SampleClass {
public string SayHello(string name) {
return $"Hello, {name}!";
}
}
// 使用 Reflection 來動態調用方法
using System;
using System.Reflection;
class Program {
static void Main() {
// 創建 SampleClass 類別的實例
Type sampleType = typeof(SampleClass);
object sampleInstance = Activator.CreateInstance(sampleType);
// 獲取 SayHello 方法資訊
MethodInfo methodInfo = sampleType.GetMethod("SayHello");
// 動態調用 SayHello 方法
object result = methodInfo.Invoke(sampleInstance, new object[] { "World" });
Console.WriteLine(result); // 輸出: Hello, World!
}
}
在上述範例中,我們使用 Activator.CreateInstance
來創建類別的實例,並使用 MethodInfo.Invoke
來調用方法
SayHello
。
System::Management::ManagementClass
是 .NET Framework 中提供用於操作 Windows Management Instrumentation (WMI) 的類別之一。它允許開發者讀取、操作和管理系統資訊,例如硬體、作業系統、網路設定等。
System::Management
是位於 System.Management.dll
中的命名空間,使用此功能需先加入參考。
// C++/CLI 寫法
using namespace System;
using namespace System::Management;
int main() {
try {
ManagementClass^ mc = gcnew ManagementClass("Win32_OperatingSystem");
for each (ManagementObject^ mo in mc->GetInstances()) {
Console::WriteLine("OS 名稱: {0}", mo["Caption"]);
}
}
catch (Exception^ ex) {
Console::WriteLine("錯誤: {0}", ex->Message);
}
return 0;
}
System.Management.dll
的參考。ManagementException
。ManagementObject
ManagementObjectSearcher
ManagementBaseObject
net stop winmgmt winmgmt /resetrepository net start winmgmt
Get-WmiObject Win32_NetworkAdapterConfiguration方式二:WMI 測試工具 (wbemtest)
wbemtest
root\cimv2
並連線SELECT * FROM Win32_NetworkAdapterConfiguration
DISM /Online /Cleanup-Image /RestoreHealth
修復系統映像大多數現代 Chromebook 支援透過 Crostini 專案來運行 Linux 應用程式。
設定 > 進階 > 開發人員 > 啟用 Linux (Beta)
。sudo apt update
sudo apt install -y dotnet-sdk-7.0
無需在本地安裝任何軟體,即可在 Chromebook 上使用雲端平台運行 .NET 程式。
Chromebook 支援使用容器運行應用程式,您可以透過 Docker 啟動 .NET 環境。
docker pull mcr.microsoft.com/dotnet/runtime:7.0
從 .NET 6 開始,應用程式可以跨多平台運行,包括 Linux。將您的應用程式編譯為跨平台格式並部署至 Chromebook。
dotnet publish -c Release -r linux-x64 --self-contained
using System;
using System.Windows.Forms;
public class MyForm : Form {
public MyForm() {
Button btn = new Button();
btn.Text = "點我";
btn.Click += (s, e) => MessageBox.Show("你點了按鈕!");
Controls.Add(btn);
}
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.Run(new 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>
</Window>
// MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e) {
MessageBox.Show("你點了按鈕!");
}
// MainPage.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>
// MainPage.xaml.cs
private void OnButtonClicked(object sender, EventArgs e) {
await DisplayAlert("通知", "你點了按鈕!", "OK");
}
技術 | 用途 | 平台 |
---|---|---|
WinForms | 快速開發桌面應用 | Windows |
WPF | 複雜桌面應用、MVVM 模式 | Windows |
MAUI | 跨平台應用 | Windows、macOS、Android、iOS |
Blazor | Web 前端開發 | 跨平台(Web) |
以下範例展示了如何使用 Controls->GetEnumerator()
方法來逐一遍歷 .NET 表單中的所有子控制項並批量修改其屬性。
using System;
using System.Drawing;
using System.Windows.Forms;
public class FormExample : Form
{
public FormExample()
{
// 初始化一些控制項
Button button1 = new Button { Text = "Button 1", Location = new Point(10, 10) };
TextBox textBox1 = new TextBox { Location = new Point(10, 50) };
Controls.Add(button1);
Controls.Add(textBox1);
// 使用 GetEnumerator() 遍歷並修改控制項屬性
ModifyControls();
}
private void ModifyControls()
{
var enumerator = Controls.GetEnumerator();
while (enumerator.MoveNext())
{
Control control = (Control)enumerator.Current;
// 設定範例:將所有控制項的背景色設為淺藍
control.BackColor = Color.LightBlue;
// 若控制項為 TextBox,將其設為不可編輯
if (control is TextBox)
{
control.Enabled = false;
}
}
}
// 啟動應用程式
public static void Main()
{
Application.Run(new FormExample());
}
}
**GetEnumerator()**: 利用 `Controls.GetEnumerator()` 方法來取得控制項的列舉器,這樣可以遍歷所有子控制項。
**條件修改**: 在 `while` 迴圈中,對每個 `Control` 物件進行屬性修改,例如將背景色設為淺藍,並根據控制項類型進行特定修改,如將 `TextBox` 設為不可編輯。
**用途**: 這種方法在需要批量修改屬性時非常有效,比如調整 UI 風格或在特定情況下禁用多個控制項。
執行後,所有控制項的背景色會變成淺藍色,且所有 `TextBox` 控制項將被設為不可編輯。
當堆疊追蹤中出現以下錯誤訊息時,開發者可能需要檢查 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
的內容。
protected override void WndProc(ref Message m)
{
try
{
// 記錄 Message 的內容
Console.WriteLine($"Message Details: hWnd={m.HWnd}, Msg={m.Msg}, WParam={m.WParam}, LParam={m.LParam}");
base.WndProc(ref m);
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex}");
throw; // 保持原有例外行為
}
}
這段程式碼會在每次接收到訊息時記錄相關資訊,並允許開發者進一步分析。
如果錯誤發生在 Dispose
方法中,可以在方法內加入例外處理來檢查相關資訊。
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
// 釋放資源
}
base.Dispose(disposing);
}
catch (Exception ex)
{
Console.WriteLine($"Exception in Dispose: {ex}");
throw;
}
}
這樣可以確保在釋放資源時不會忽略重要的錯誤資訊。
如果無法確定錯誤發生的位置,可以透過全域例外處理來記錄堆疊追蹤和相關資訊。
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
:訊息的 ID。WParam
:附加訊息資訊(字參數)。LParam
:附加訊息資訊(長參數)。WndProc
或修改程式碼需小心,避免影響現有行為。Message
的座標或 ID 不固定,可考慮加入條件判斷來篩選需要的訊息。在 .NET C++/CLI 中,WmClose(Message& m)
是 System.Windows.Forms.Form
的內部保護方法,無法直接覆寫。不過,可以透過覆寫 WndProc
方法來攔截並處理 WM_CLOSE
訊息,以達到類似覆寫 WmClose
的效果。
#include <Windows.h>
#include <System.Windows.Forms.h>
using namespace System;
using namespace System::Windows::Forms;
public ref class CustomForm : public Form
{
protected:
// 模擬 WmClose 行為的覆寫
void WmClose(Message% m)
{
// 在這裡加入自定義的 WM_CLOSE 行為
if (MessageBox::Show("確定要關閉視窗嗎?", "確認", MessageBoxButtons::YesNo) == DialogResult::Yes)
{
// 繼續調用基底行為以進行正常關閉
this->Form::WndProc(m);
}
else
{
// 阻止關閉視窗
return;
}
}
// 覆寫 WndProc,攔截 WM_CLOSE 訊息並調用 WmClose
virtual void WndProc(Message% m) override
{
const int WM_CLOSE = 0x0010;
if (m.Msg == WM_CLOSE)
{
WmClose(m); // 調用自定義的 WmClose 方法
}
else
{
// 處理其他訊息
Form::WndProc(m);
}
}
};
[STAThread]
int main(array<String^>^ args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
CustomForm^ form = gcnew CustomForm();
form->Text = "覆寫 WmClose 行為範例";
Application::Run(form);
return 0;
}
模擬覆寫 WmClose
方法,在這裡自定義視窗關閉的行為。透過確認對話框詢問使用者是否要關閉視窗。
覆寫 WndProc
方法來攔截 WM_CLOSE
訊息,並將其委派給自定義的 WmClose
方法。
在沒有攔截 WM_CLOSE
的情況下,調用基底類別的 WndProc
方法處理其他訊息。
WmClose
方法僅是模擬覆寫,實際上是透過攔截訊息進行處理。Form::WndProc
以確保基底行為正常運作。InvokeRequired
是 System::Windows::Forms::Control
的屬性,用來判斷目前執行緒是否為控制項所屬的 UI 執行緒。當從背景執行緒存取 UI 控件時,應使用 Invoke
將操作封送回 UI 執行緒。
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Drawing;
ref class ImageHelper {
public:
static void SetImageSafe(PictureBox^ pPictureBox, Bitmap^ b) {
if (pPictureBox == nullptr)
return;
if (pPictureBox->InvokeRequired) {
// 使用 MethodInvoker 呼叫同一函式,但於 UI 執行緒執行
pPictureBox->Invoke(
gcnew MethodInvoker(gcnew Action
InvokeRequired
只能用於繼承 Control
的物件(如 PictureBox)。true
,代表目前執行緒不是 UI 執行緒,必須用 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
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Drawing;
void SafeAssignImage(PictureBox^ pPictureBox, Bitmap^ b) {
if (pPictureBox->InvokeRequired) {
pPictureBox->Invoke(gcnew MethodInvoker(gcnew delegate {
try {
if (b != nullptr) {
if (pPictureBox->Image != nullptr)
delete pPictureBox->Image;
pPictureBox->Image = b;
}
} catch (System::Exception^ ex) {
Console::WriteLine("設定圖片失敗: " + ex->Message);
}
}));
} else {
try {
if (b != nullptr) {
if (pPictureBox->Image != nullptr)
delete pPictureBox->Image;
pPictureBox->Image = b;
}
} catch (System::Exception^ ex) {
Console::WriteLine("設定圖片失敗: " + ex->Message);
}
}
}
gcnew MethodInvoker(...)
必須是合法的委派,不支援 C++11 lambda(如 [=]())語法。BeginInvoke
搭配 Object^
陣列並解析。Blazor 是由微軟推出的前端框架,允許開發者使用 C# 和 Razor 語法來建立互動式 Web 應用程式,無需使用 JavaScript,即可在瀏覽器中執行 C# 程式碼。
Blazor 無縫整合 ASP.NET Core,可與現有的 .NET 應用程式結合,並支援依賴注入、路由、元件架構等功能。
對於以 .NET 為主要技術的開發者而言,Blazor 提供了與 Node.js 統一 JavaScript 語言相對應的解決方案,使前後端都能使用 C#,建立一致性更高的開發體驗。
.NET MAUI(Multi-platform App UI)是微軟推出的跨平台應用框架,可使用 C# 和 XAML 撰寫一次程式碼,部署到 Windows、Android、iOS 甚至 macOS。
可使用 .NET MAUI 的 Blazor Hybrid 模式,在原生應用中嵌入 Web UI,讓開發者用 Razor 組件開發跨平台使用者介面,仍可存取原生 API。
.NET MAUI 是目前最完整的 .NET 跨平台解決方案,可結合 Blazor 達成 Web、桌面與行動平台的統一應用開發,提供給 .NET 開發者一個語言與技術一致的全端體驗。
.NET MAUI 可透過 API 呼叫與 ASP.NET Core 建立的後端服務整合,形成完整的「前端 App + 後端 API」架構。
ASP.NET Core 解決「後端服務」與「Web 應用」問題,.NET MAUI 則專注在「用戶端跨平台 App」。兩者並不互斥,反而常搭配使用,構成完整解決方案。
以下是一個使用 .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="你尚未點擊按鈕"
FontSize="24"
HorizontalOptions="Center" />
<Button Text="點我"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentPage>
namespace MauiApp;
public partial class MainPage : ContentPage
{
int count = 0;
public MainPage()
{
InitializeComponent();
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
counterLabel.Text = $"你已經點擊了 {count} 次";
}
}
這個簡單的範例展示了 .NET MAUI 的跨平台能力,開發者只需撰寫一次 XAML 與 C# 程式碼,即可同時在多個平台運作。
MainPage.xaml
:定義 UI 介面(使用 XAML)MainPage.xaml.cs
:後端程式碼(C#)處理事件MauiProgram.cs
:應用程式啟動與服務註冊點Platforms
資料夾:各平台的原生設定(Android、iOS、Windows、MacCatalyst)使用 Visual Studio 建立 .NET MAUI 專案只需幾個步驟,且能一套程式碼同時部署到多個平台,是現代 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"
VerticalOptions="Center">
<Label
Text="歡迎使用 .NET MAUI!"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center" />
<Button
x:Name="counterBtn"
Text="點我!"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentPage>
using System;
namespace MyMauiApp;
public partial class MainPage : ContentPage
{
int count = 0;
public MainPage()
{
InitializeComponent();
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
counterBtn.Text = $"你已經點了 {count} 次";
}
}
Spacing
(間距)與 Padding
(內距)。x:Name
以供 C# 後端存取,Clicked
屬性指定點擊事件處理方法。InitializeComponent()
:初始化 XAML 中定義的 UI 結構。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 =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
MauiApp
實例。App.xaml.cs
中的 App 類別),這會是應用程式的根組件。builder.Services.AddSingleton<MainPage>();這樣可讓 MainPage 使用依賴注入方式載入,例如在 App 類中使用
serviceProvider.GetService<MainPage>()
。
builder.Services.AddTransient<MyViewModel>(); builder.Services.AddSingleton<IMyService, MyService>();
MauiProgram.cs
就像 ASP.NET Core 的 Startup.cs
或 Program.cs
,是應用啟動與服務註冊的主要設定中心。開發大型 MAUI 應用時,擴充此檔案來加入 DI、日誌、組件設定等是非常常見的做法。
email: [email protected]