c# WPF實(shí)現(xiàn)Windows資源管理器(附源碼)
今天我來寫一篇關(guān)于利用WPF來實(shí)現(xiàn)Windows的資源管理器功能,當(dāng)然只是局部實(shí)現(xiàn)這個(gè)功能,因?yàn)樵诤芏鄷r(shí)候我們需要來實(shí)現(xiàn)對(duì)本機(jī)資源的管理,當(dāng)然我們可以使用OpenFileDialog dialog = new OpenFileDialog()這個(gè)Microsoft.Win32命名空間下的這個(gè)類來實(shí)現(xiàn)一些資源查找和導(dǎo)入的功能,但是在很多時(shí)候我們可能需要更多的功能,并且希望能夠集成到我們自己的項(xiàng)目中,但是我們這個(gè)時(shí)候就不得不自己來寫一套來集成到我們的軟件中去了,因?yàn)镺penFileDialog這個(gè)是無法作為一個(gè)UserControl加入到我們的項(xiàng)目中的,當(dāng)然我們只是實(shí)現(xiàn)了其中的一部分功能,因?yàn)閃indows的資源管理器也是一個(gè)重量級(jí)的應(yīng)用,也是十分龐大和復(fù)雜的,這里只是通過這個(gè)Demo來加深對(duì)WPF的MVVM模式及軟件基本功的鞏固。
在正式介紹整體框架之前,首先來看看整體的結(jié)構(gòu),從而對(duì)其有一個(gè)大概的了解。

整個(gè)界面從大的方面來說主要包括三個(gè)方面:1 文件及文件夾顯示區(qū)域、2 導(dǎo)航區(qū)域、3 路徑顯示區(qū)域,其實(shí)在整個(gè)界面中,2和3都是圍繞1來進(jìn)行操作的,三個(gè)區(qū)域之間的耦合性其實(shí)是非常高的,所以常規(guī)的做法就是三個(gè)部分分為三個(gè)UserControl,并且同時(shí)綁定到一個(gè)ViewModel中,這樣整個(gè)層次也就比較清晰了,缺點(diǎn)是一個(gè)ViewModel中代碼太多,職責(zé)非常大,所以在這個(gè)DEMO中嘗試將三個(gè)部分分開,三個(gè)ViewModel來操作三個(gè)View里面的內(nèi)容,整個(gè)實(shí)現(xiàn)下來其實(shí)也有一些不足之處,那就是容易將問題復(fù)雜化,很多在一個(gè)類中就能夠完成的工作,最終要通過各種類與類之間的耦合來完成,所以通過這個(gè)DEMO希望自己能夠多一些思考,從而在軟件的設(shè)計(jì)中能夠再多一些經(jīng)驗(yàn),能夠把握好軟件粒度的問題,下面就軟件的具體內(nèi)容來深入分析一下。
第一部分:FileList
這個(gè)部分是整個(gè)文件和文件夾的顯示部分,再三權(quán)衡下,決定采用自定義DataGrid的方式來展現(xiàn)整個(gè)部分。
<UserControl x:Class="FileSelectorDemo.Views.FileList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:converter="clr-namespace:FileSelectorDemo.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:defines="clr-namespace:FileSelectorDemo.Defines"
xmlns:local="clr-namespace:FileSelectorDemo.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<converter:CountToVisibilityConverter x:Key="CountToVisibilityConverter"></converter:CountToVisibilityConverter>
<converter:TypeToVisibleConverter x:Key="TypeToVisibleConverter"></converter:TypeToVisibleConverter>
<converter:TypeToCollapsedConverter x:Key="TypeToCollapsedConverter"></converter:TypeToCollapsedConverter>
<converter:CollectionSelectedCountConverter x:Key="CollectionSelectedCountConverter"></converter:CollectionSelectedCountConverter>
</UserControl.Resources>
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid>
<StackPanel Orientation="Vertical">
<DataGrid x:Name="fileList" Style="{StaticResource DefaultDataGrid}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" defines:MouseDoubleClick.Command="{Binding OpenCurrentDirectory}"
defines:MouseDoubleClick.CommandParameter="{Binding SelectedItem,RelativeSource={RelativeSource Self}}" IsReadOnly="True"
defines:MouseLeftButtonUpClick.Command="{Binding SelectCurrentFileListItem}" ItemsSource="{Binding CurrentFileList}" CanUserAddRows="False" AutoGenerateColumns="False" GridLinesVisibility="None">
<DataGrid.Columns>
<DataGridTemplateColumn MinWidth="60">
<DataGridTemplateColumn.Header>
<CheckBox Content="全選" Margin="2" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding DataContext.IsStateCheckAll,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}"></CheckBox>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="不可選" FontSize="12" Foreground="Black" Visibility="{Binding CurrentType,Converter={StaticResource TypeToCollapsedConverter}}" HorizontalAlignment="Left" VerticalAlignment="Center" ></TextBlock>
<CheckBox IsChecked="{Binding IsSelected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" IsThreeState="False" IsHitTestVisible="False" Visibility="{Binding CurrentType,Converter={StaticResource TypeToVisibleConverter}}" HorizontalAlignment="Left" VerticalAlignment="Center" ToolTip="點(diǎn)擊選中當(dāng)前對(duì)象"></CheckBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="名稱" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
<Image Source="{Binding Icon}" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"></Image>
<TextBlock Text="{Binding Name}" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="修改日期" Binding="{Binding CreateTime}"/>
<DataGridTextColumn Header="類型" Binding="{Binding CurrentType}"/>
<DataGridTextColumn Header="大小" Binding="{Binding Size}"/>
</DataGrid.Columns>
</DataGrid>
<TextBlock Margin="2 5" HorizontalAlignment="Left" VerticalAlignment="Center" >
<Run>總共 </Run>
<Run Text="{Binding CurrentFileList.Count,Mode=OneWay}"></Run>
<Run> 個(gè)項(xiàng)目</Run>
<Run>(已選中 </Run>
<Run Text="{Binding CurrentFileList,Converter={StaticResource CollectionSelectedCountConverter},Mode=OneWay}"></Run>
<Run> 個(gè)項(xiàng)目)</Run>
</TextBlock>
</StackPanel>
<TextBlock Text="該文件為空" Visibility="{Binding CurrentFileList.Count,Converter={StaticResource CountToVisibilityConverter}}" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>
這里都是一些常規(guī)的定義,這里就不再贅述,DataGrid樣式是引用Themes文件夾下面的CustomDataGrid的樣式,這里面也包括自定義的ScrollViewer樣式和ScrollBar的樣式,讀者也可以進(jìn)行思考,另外需要注意設(shè)置下面的幾個(gè)屬性。
1 IsReadOnly="True"這個(gè)屬性能夠保證在鼠標(biāo)點(diǎn)擊時(shí)候,不再顯示內(nèi)部的TextBox,從而使使用者不能夠隨意進(jìn)行編輯。
2 GridLinesVisibility="None" 這個(gè)能夠使整個(gè)DataGrid不再顯示分割線,從而使其樣式更加接近Windows的原生樣式。
3 CanUserAddRows="False" 這個(gè)屬性也非常重要,不然在整個(gè)顯示區(qū)域的下面會(huì)多出一行,當(dāng)用戶點(diǎn)擊它的時(shí)候會(huì)增加行。
4 AutoGenerateColumns="False"這個(gè)就比較熟悉了,一般不讓其自動(dòng)增加列。
5 SelectionUnit=“FullRow” 表示鼠標(biāo)點(diǎn)擊時(shí)選擇的單位是整行,而不是其中的單元格或者其他,關(guān)于其它的幾個(gè)枚舉值,讀者也可查閱相關(guān)了解。
6 SelectionMode=“Extended”允許多選,當(dāng)按下鼠標(biāo)的Ctrl鍵進(jìn)行點(diǎn)擊的時(shí)候能夠選中多個(gè)對(duì)象。
7 最后一個(gè)就是關(guān)于設(shè)置DataGrid的虛擬化容器了,具體設(shè)置方法是:
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"></Setter> <Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling" />
WPF中的VirtualizingStackPanel.VirtualizationMode 附加屬性指定 ItemsControl 中的面板如何虛擬化其子項(xiàng)。默認(rèn)情況下,VirtualizingStackPanel 將為每個(gè)可見項(xiàng)創(chuàng)建一個(gè)項(xiàng)容器,并在不再需要時(shí)(比如當(dāng)項(xiàng)滾動(dòng)到視圖之外時(shí))丟棄該容器。當(dāng) ItemsControl 包含多個(gè)項(xiàng)時(shí),創(chuàng)建和廢棄項(xiàng)容器的過程可能會(huì)對(duì)性能產(chǎn)生負(fù)面影響。如果 VirtualizingStackPanel.VirtualizationMode 設(shè)置為 Recycling,VirtualizingStackPanel 將重用項(xiàng)容器,而不是每次都創(chuàng)建新的項(xiàng)容器,這個(gè)是摘錄自MSDN的相關(guān)資料,由于我們加載的DataGrid的項(xiàng)不是很多,如果足夠多的情況下,效果可能會(huì)更加明顯,關(guān)于更多的“虛擬化”技術(shù)可以參考更多的資料。
這里我們需要重點(diǎn)關(guān)注的是當(dāng)我們雙擊DataGridRow時(shí)會(huì)打開對(duì)應(yīng)的子文件夾,同時(shí)單擊時(shí)會(huì)選中當(dāng)前的DataGridRow,這里關(guān)于事件的綁定我們使用的不是System.Windows.Interactivity這種方式來綁定事件的,這里我們通過自定義一個(gè)附加屬性來實(shí)現(xiàn)的,這里以鼠標(biāo)左鍵雙擊為例來進(jìn)行說明。
這里需要進(jìn)行說明的就是在OnMouseDoubleClick中,我們通過當(dāng)前鼠標(biāo)的點(diǎn)擊的Point來查找最終的DataGridRow 然后觸發(fā)綁定的Command事件,在前臺(tái)View中,我們只需要通過綁定到對(duì)應(yīng)的ICommand即可。
public class MouseDoubleClick
{
public static DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command",
typeof(ICommand),
typeof(MouseDoubleClick),
new UIPropertyMetadata(CommandChanged));
public static DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter",
typeof(object),
typeof(MouseDoubleClick),
new UIPropertyMetadata(null));
public static void SetCommand(DependencyObject target, ICommand value)
{
target.SetValue(CommandProperty, value);
}
public static void SetCommandParameter(DependencyObject target, object value)
{
target.SetValue(CommandParameterProperty, value);
}
public static object GetCommandParameter(DependencyObject target)
{
return target.GetValue(CommandParameterProperty);
}
private static void CommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
Control control = target as Control;
if (control != null)
{
if ((e.NewValue != null) && (e.OldValue == null))
{
control.MouseDoubleClick += OnMouseDoubleClick;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
control.MouseDoubleClick -= OnMouseDoubleClick;
}
}
}
private static void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
DataGrid datagrid = sender as DataGrid;
Point point = e.GetPosition(datagrid);
IInputElement obj = datagrid.InputHitTest(point);
DependencyObject target = obj as DependencyObject;
while (target != null)
{
if (target is DataGridRow)
{
ICommand command = (ICommand)datagrid.GetValue(CommandProperty);
object commandParameter = datagrid.GetValue(CommandParameterProperty);
if (null != commandParameter)
{
command.Execute(commandParameter);
}
break;
}
target = VisualTreeHelper.GetParent(target);
}
}
}
defines:MouseDoubleClick.Command="{Binding OpenCurrentDirectory}" defines:MouseDoubleClick.CommandParameter="{Binding SelectedItem,RelativeSource={RelativeSource Self}}"
其中我們需要定義命名空間defines,這種方法為我們綁定View層中的各種事件提供能了一種新的方式,這個(gè)是我們需要不斷去總結(jié)和分析的地方。
第二部分:Navigation
這一部分是我們的導(dǎo)航欄的部分,通過向前、向后、向上等快捷操作,我們能夠快速切換文件夾,從而使切換路徑變得更加容易,這一部分就是需要著重說一下最近瀏覽項(xiàng)目這個(gè)功能,它能夠保存用戶最近瀏覽的10個(gè)目錄(開發(fā)者自定義),從而方便用戶迅速切換不同的路徑,這個(gè)是通過ToggleButton和Popup這一對(duì)經(jīng)典的組合來實(shí)現(xiàn)的,具體實(shí)現(xiàn)請(qǐng)參考源代碼。這一部分重點(diǎn)來說一下當(dāng)鼠標(biāo)移動(dòng)到不同的位置時(shí),能夠變換綁定的圖標(biāo)是向前還是向后抑或選中狀態(tài),這個(gè)其實(shí)是通過綁定每一個(gè)Model的一個(gè)CurrentDirection來實(shí)現(xiàn)的,這里需要重點(diǎn)掌握DataTrigger的使用方法。
<Popup Placement="Bottom" PlacementTarget="{Binding ElementName=NavigationPanel}" StaysOpen="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsOpen="{Binding IsChecked,ElementName=History,Mode=OneWay}" PopupAnimation="Slide">
<ItemsControl Background="#f5f5f5" BorderBrush="Gray" BorderThickness="1" Padding="0"
ItemsSource="{Binding AttachedDataContext.DirectoryHistory,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}}">
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border x:Name="outer" Padding="0 2" Background="#f5f5f5">
<StackPanel Orientation="Vertical" IsItemsHost="True"></StackPanel>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="radioButton" Command="{Binding AttachedDataContext.SwitchDirectory,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding}">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="bg" Padding="0" Background="#f5f5f5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
<Path x:Name="path" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" Width="24" Height="24"
Opacity="0" StrokeThickness="2" StrokeLineJoin="Round" SnapsToDevicePixels="False">
</Path>
<Image Source="{Binding Icon}" Width="24" Height="24" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"></Image>
<TextBlock Text="{Binding Name}" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding CurrentDirection}" Value="選中">
<Setter Property="Opacity" Value="1" TargetName="path"></Setter>
<Setter Property="Data" Value="M 2,10 L 8,14 18,6" TargetName="path"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CurrentDirection}" Value="向前">
<Setter Property="Opacity" Value="0" TargetName="path"></Setter>
<Setter Property="Data" Value="M8,6 L1,11 8,16 M0,11 L15,11" TargetName="path"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CurrentDirection}" Value="向后">
<Setter Property="Opacity" Value="0" TargetName="path"></Setter>
<Setter Property="Data" Value="M8,6 L15,11 8,16 M0,11 L15,11" TargetName="path"></Setter>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="#91c9f7" TargetName="bg"></Setter>
<Setter Property="Opacity" Value="1" TargetName="path"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Popup>
第三部分:BreadCrumbView
這個(gè)部分是用來顯示當(dāng)前文件夾路徑,并進(jìn)行快速切換準(zhǔn)備的,這個(gè)部分是一個(gè)組合控件,主要是通過ItemsControl和ToggleButton和Popup來實(shí)現(xiàn)的,這個(gè)里面需要注意的是這里面綁定的命令和方法都是在FileList的ViewModel中定義的,這里為了最大程度的實(shí)現(xiàn)代碼的重用。很多時(shí)候我們會(huì)發(fā)現(xiàn)通過這種方式我們需要能夠隨時(shí)訪問到FileListViewModel中的內(nèi)容,這個(gè)是整個(gè)DEMO中最重要的部分,所以如何才能夠引用到FileListViewModel里面的內(nèi)容呢?
public partial class BreadCrumbView : UserControl
{
public BreadCrumbView()
{
InitializeComponent();
Loaded +=new RoutedEventHandler(BreadCrumbView_Loaded);
}
private void BreadCrumbView_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new ViewModels.BreadCrumbViewModel(AttachedDataContext);
}
/// <summary>
/// 當(dāng)前FileList的DataContext對(duì)象
/// </summary>
public object AttachedDataContext
{
get { return (object)GetValue(AttachedDataContextProperty); }
set { SetValue(AttachedDataContextProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AttachedDataContextProperty =
DependencyProperty.Register("AttachedDataContext", typeof(object), typeof(BreadCrumbView), new PropertyMetadata(null));
}
通過定義一個(gè)AttachedDataContext對(duì)象,我們能夠?qū)ileListViewModel中定義的屬性分散到各個(gè)ViewModel中,這樣在一定程度上能夠保證避免FileListViewModel中代碼過多同時(shí)職責(zé)過重的問題,但是同時(shí)我們也發(fā)現(xiàn)了,如果彼此之間的耦合過大,采用這種方式會(huì)加重代碼之間的復(fù)雜度,因?yàn)橛袝r(shí)不得不通過Action或者事件等方式來進(jìn)行ViewModel之間的交互和通訊,所以降到這里我們不得不說一些較大較復(fù)雜的項(xiàng)目中使用框架的重要性了,比如Prism亦或是Caliburn.Micro等框架能夠使整個(gè)軟甲架構(gòu)看起來更加清楚和明白,這也是為了更好地增加軟件的模塊化和靈活性。
通過這個(gè)DEMO的分析,我們需要在不斷的實(shí)踐中去總結(jié)這類型的經(jīng)驗(yàn),從而使整個(gè)軟件顯得更加合理,最終使自己能夠真正地對(duì)軟件的架構(gòu)的思想有一個(gè)比較深入的了解。
最后需要整個(gè)Demo的請(qǐng)點(diǎn)擊此處進(jìn)行下載!
以上就是c# WPF實(shí)現(xiàn)Windows資源管理器(附源碼)的詳細(xì)內(nèi)容,更多關(guān)于c# WPF實(shí)現(xiàn)Windows資源管理器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- c# 基于wpf,開發(fā)OFD電子文檔閱讀器
- c# WPF中自定義加載時(shí)實(shí)現(xiàn)帶動(dòng)畫效果的Form和FormItem
- c# WPF中CheckBox樣式的使用總結(jié)
- C# WPF 自定義按鈕的方法
- c# WPF中通過雙擊編輯DataGrid中Cell的示例(附源碼)
- C# WPF Image控件的綁定方法
- c# WPF設(shè)置軟件界面背景為MediaElement并播放視頻
- 在C# WPF下自定義滾動(dòng)條ScrollViewer樣式的操作
- C#中WPF依賴屬性的正確學(xué)習(xí)方法
- C# WPF上位機(jī)實(shí)現(xiàn)和下位機(jī)TCP通訊的方法
- C# WPF使用AForge類庫操作USB攝像頭拍照并保存
- c# wpf使用GMap.NET類庫,實(shí)現(xiàn)地圖軌跡回放
相關(guān)文章
C#實(shí)現(xiàn)Word轉(zhuǎn)換TXT的方法詳解
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)Word轉(zhuǎn)換TXT的功能,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12
C#無損高質(zhì)量壓縮圖片實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了C#無損高質(zhì)量壓縮圖片的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
c#?使用線程對(duì)串口serialPort進(jìn)行收發(fā)數(shù)據(jù)(四種)
本文主要介紹了c#?使用線程對(duì)串口serialPort進(jìn)行收發(fā)數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
解析美國東部時(shí)間與北京時(shí)間相互轉(zhuǎn)換的實(shí)現(xiàn)代碼
本篇文章是對(duì)美國東部時(shí)間與北京時(shí)間相互轉(zhuǎn)換的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
講解C#面相對(duì)象編程中的類與對(duì)象的特性與概念
這篇文章主要介紹了C#面相對(duì)象編程中的類與對(duì)象的特性與概念,OOP面向?qū)ο笳Z言相對(duì)C語言這樣面相過程的語言來說具有類和對(duì)象以及方法這樣的特性,需要的朋友可以參考下2016-01-01
C# 本地函數(shù)與 Lambda 表達(dá)式詳細(xì)介紹
這篇文章主要介紹了C 語言本地函數(shù)與 Lambda 表達(dá)式,,由于 C# 長期以來一直使用 lambda,因此從差異和相似之處來看本地函數(shù)確實(shí)是有意義的,感興趣的小伙伴可以參考下面文章內(nèi)容2021-09-09

