Action 封裝一個沒有參數或 1 ~ 16 個參數且不返回值的方法。Func 封裝一個沒有參數或 1 ~ 16 個參數且有指定類型的返回值的方法。
public delegate void Action ( );
public delegate void Action ( T1 ~ T16 );
public delegate TResult Func < out TResult > ( ) where TResult : allows ref struct;
public delegate TResult Func < out TResult > ( T1 ~ T16 ) where TResult : allows ref struct;
參數
| 參數 | 類型 | 註解 |
|---|---|---|
| T1 ~ T16 | 任意類型(逆變) |
返回值
| 註解 | 説明 |
|---|---|
| 僅限於 Func | 任意類型的返回值(協變) |
備註
你可以使用此委託將方法作為參數傳遞,而無需顯式聲明自定義委託。被封裝的方法必須與該委託定義的方法簽名相符。這意味着被封裝的方法必須沒有參數,若是 Action 沒有返回值;若是 Func 有返回值(在 C# 中,Action 方法必須返回 void;在 F# 中,函數或方法必須返回 unit;在 Visual Basic 中,Action 必須由 Sub………End Sub 結構定義,Func 必須由 Function……End Function 定義。Action 也可以是一個返回值被忽略的方法)。通常,Action 方法用於執行某項操作。
使用 Action 委託時,不必顯式定義封裝無參數過程的委託。例如,下面的代碼顯式聲明瞭一個名為 FF查看值 的委託,並將對 LEI名稱 . FF顯示到控制枱 實例方法的引用分配給其委託實例。
delegate void FF查看值 ( );
public class TestTestDelegate
{
public static void Main ( )
{
LEI名稱 mc = new ( "Koani" );
FF查看值 FF查看方法 = mc . FF顯示到控制枱;
FF查看方法 ( );
}
}
class LEI名稱
{
private readonly string zfc實例名稱;
public LEI名稱 ( string 名稱 )
{
zfc實例名稱 = 名稱;
}
public void FF顯示到控制枱 ( )
{
Console . WriteLine ( zfc實例名稱 );
}
}
以下示例通過實例化 Action 委託(而非顯式定義新委託併為其分配命名方法)來簡化上一個示例的代碼。
public class TestTestDelegate
{
public static void Main ( )
{
LEI名稱 mc = new ( "Koani" );
Action FF查看方法 = mc . FF顯示到控制枱;
FF查看方法 ( );
}
}
class LEI名稱
{
private readonly string zfc實例名稱;
public LEI名稱 ( string 名稱 )
{
zfc實例名稱 = 名稱;
}
public void FF顯示到控制枱 ( )
{
Console . WriteLine ( zfc實例名稱 );
}
}
在 C# 中,您也可以將 Action 委託與匿名方法(可以簡化為本地函數)一起使用,如下例所示。
public class TestTestDelegate
{
public static void Main ( )
{
LEI名稱 mc = new ( "Koani" );
void FF查看方法 ( ) { mc . FF顯示到控制枱 ( ); } // Action FF查看方法 = delegate ( ) { mc . FF顯示到控制枱 (); }; 和 Action FF查看方法 = ( ) => mc . FF顯示到控制枱 ( ); 的 C# 7.0 以上推薦形式(使用本地函數)
FF查看方法 ( );
}
}
class LEI名稱
{
private readonly string zfc實例名稱;
public LEI名稱 ( string 名稱 )
{
zfc實例名稱 = 名稱;
}
public void FF顯示到控制枱 ( )
{
Console . WriteLine ( zfc實例名稱 );
}
}
您也可以將 lambda 表達式分配給 Action 委託實例,如上例所示。
以下示例演示瞭如何使用不接受參數的委託。此代碼創建一個名為 LEI粗心值 的泛型類,該類包含一個 Func < TResult > 類型的字段。該委託字段可以存儲對任何函數的引用,這些函數返回的值的類型與 LEI粗心值 對象的類型參數相對應。LEI粗心值 類型還具有一個 值 屬性,該屬性會執行該函數(如果尚未執行)並返回結果值。
該示例創建了兩個方法,並使用調用這些方法的 lambda 表達式實例化了兩個 LazyValue 對象。lambda 表達式不接受參數,因為它們只需要調用一個方法。如輸出所示,這兩個方法僅在檢索每個 LazyValue 對象的值時才會執行。
LEI延遲加載值 < int > zhs延遲整數 = new ( ( ) => FF計算延遲1 ( ) );
LEI延遲加載值 < long > czhs延遲長整數 = new ( ( ) => FF計算延遲2 ( "雞蛋碰石頭" ) );
LEI延遲加載值 < byte > zj延遲字節 = new ( ( ) => FF計算延遲3 ( "123" ) );
Console . WriteLine ( "延遲加載對象被創建了。" );
Console . WriteLine ( zhs延遲整數 . 值 );
Console . WriteLine ( czhs延遲長整數 . 值 );
Console . WriteLine ( zj延遲字節 . 值 );
static int FF計算延遲1 ( )
{
Console . WriteLine ( "\n計算成本高1 ( ) 被執行。" );
return 1;
}
static long FF計算延遲2 ( string 輸入值 )
{
Console . WriteLine ( "\n計算成本高2 ( ) 被執行。" );
return ( long ) 輸入值 . Length;
}
static byte FF計算延遲3 ( string 輸入值 )
{
Console . WriteLine ( "\n計算成本高3 ( ) 被執行。" );
return ( ( byte ) 輸入值 [ 0 ] );
}
class LEI延遲加載值 < T > ( Func < T > lambda表達式 ) where T : struct
{
private Nullable < T > _值 = null;
private readonly Func < T > FF獲取值 = lambda表達式;
public T 值
{
get
{
_值 ??= FF獲取值 ( );
return ( T ) _值;
}
}
}
使用 Func < TResult > 委託時,無需顯式定義封裝無參數方法的委託。例如,下面的代碼顯式聲明瞭一個名為 FF寫文件 的委託,並將對 LEI目標輸出 . FF寫入文件 實例方法的引用分配給其委託實例。
LEI目標輸出 shuchu = new( );
FF寫文件 xr = shuchu . FF寫入文件;
if ( xr ( ) )
Console . WriteLine ( "成功!" );
else
Console . WriteLine ( "失敗!" );
delegate bool FF寫文件 ( );
public class LEI目標輸出
{
public bool FF寫入文件 ( )
{
try
{
string zfc測試文件路徑 = @"F:\測試文件夾\Func 測試.txt";
using StreamWriter LIU寫入 = new ( zfc測試文件路徑 );
{
LIU寫入 . WriteLine ( "嘻嘻哈哈!" );
}
return true;
}
catch { return false; }
}
}
下面的示例通過實例化 Func < TResult > 委託,而非顯式定義新委託併為其分配命名方法,來簡化此代碼。
LEI目標輸出 shuchu = new( );
Func < bool > xr = shuchu . FF寫入文件;
if ( xr ( ) )
Console . WriteLine ( "成功!" );
else
Console . WriteLine ( "失敗!" );
public class LEI目標輸出
{
public bool FF寫入文件 ( )
{
try
{
string zfc測試文件路徑 = @"F:\測試文件夾\Func 測試.txt";
using StreamWriter LIU寫入 = new ( zfc測試文件路徑 );
{
LIU寫入 . WriteLine ( "嘻嘻哈哈!" );
}
return true;
}
catch { return false; }
}
}
在 C# 中,您可以將 Func < TResult > 委託與匿名方法一起使用,如下例所示。
LEI目標輸出 shuchu = new( );
Func < bool > xr = delegate ( ) { return shuchu . FF寫入文件 ( ); };
if ( xr ( ) )
Console . WriteLine ( "成功!" );
else
Console . WriteLine ( "失敗!" );
public class LEI目標輸出
{
public bool FF寫入文件 ( )
{
try
{
string zfc測試文件路徑 = @"F:\測試文件夾\Func 測試.txt";
using StreamWriter LIU寫入 = new ( zfc測試文件路徑 );
{
LIU寫入 . WriteLine ( "嘻嘻哈哈!" );
}
return true;
}
catch { return false; }
}
}
您也可以將 lambda 表達式分配給 Func < TResult > 委託,如下例所示。
LEI目標輸出 shuchu = new( );
Func < bool > xr = ( ) => shuchu . FF寫入文件 ( );
if ( xr ( ) )
Console . WriteLine ( "成功!" );
else
Console . WriteLine ( "失敗!" );
public class LEI目標輸出
{
public bool FF寫入文件 ( )
{
try
{
string zfc測試文件路徑 = @"F:\測試文件夾\Func 測試.txt";
using StreamWriter LIU寫入 = new ( zfc測試文件路徑 );
{
LIU寫入 . WriteLine ( "嘻嘻哈哈!" );
}
return true;
}
catch { return false; }
}
}
lambda 表達式的基礎類型是泛型 Func 委託之一。這使得可以將 lambda 表達式作為參數傳遞,而無需將其顯式分配給委託。特別是,由於 System . Linq 命名空間中許多類型的方法都具有 Func 參數,因此可以向這些方法傳遞 lambda 表達式,而無需顯式實例化 Func 委託。
如果你有一個耗時的計算,希望只在確實需要結果時才執行,可以將這個耗時函數分配給一個 Func < TResult > 委託。這樣,函數的執行就可以延遲到表達式中使用訪問該值的屬性時才進行。
Action ( T ) 和 Func ( T , TResult )
以下示例演示瞭如何使用 Action < T > 委託來打印 LB < T > 對象的內容。在本示例中,FF輸出 方法用於將列表內容顯示到控制枱。此外,C# 示例還演示瞭如何使用匿名方法將內容顯示到控制枱。請注意,該示例並未顯式聲明 Action < T > 變量。相反,它將一個引用傳遞給一個接受單個參數且不返回值的方法,該方法被傳遞給 LB < T > . ForEach 方法,而 LB < T > . ForEach 方法的單個參數是一個 Action < T > 委託。同樣,在 C# 示例中,並未顯式實例化 Action < T > 委託,因為匿名方法的簽名與 LB < T > . ForEach 方法所需的 Action < T > 委託的簽名相匹配。
List < string > LB = [ "孫悟空" , "豬八戒" , "沙和尚" , "白龍馬" ];
Console . WriteLine ( "LB . foreach ( FF輸出 );" );
LB . ForEach ( FF輸出 );
Console . WriteLine ( "\nLB . foreach ( string 姓名 ) { Console . WriteLine ( 姓名 ); }" );
LB . ForEach ( delegate ( string 姓名 ) { Console . WriteLine ( 姓名 ); } );
static void FF輸出 ( string 字符串 )
{
Console . WriteLine ( 字符串 );
}
以下示例演示瞭如何聲明和使用 Func < T , TResult > 委託。此示例聲明瞭一個 Func < T , TResult > 變量,併為其分配了一個 lambda 表達式,該表達式將字符串中的字符轉換為大寫。封裝此方法的委託隨後被傳遞給 Enumerable . Select 方法,以將字符串數組中的字符串轉換為大寫。
Func < string , string > 大寫 = 字符串 => 字符串 . ToUpper ( );
string [ ] ZFCs = [ "Zhus" , "wangba" , "NiaoQun" , "紅色的" ];
IEnumerable < string > Cis = ZFCs . Select ( 大寫 );
foreach ( string c in Cis )
{
Console . WriteLine ( c );
}
using System . Linq;
string zfc1 = "第一行信息。";
string zfc2 = "第二行信息。";
Action < string , string > FF輸出字符串;
if ( Environment . GetCommandLineArgs ( ) . Any ( arg => arg . Contains ( "/f" , StringComparison . CurrentCultureIgnoreCase ) ) ) // 當命令行中包含 “/F” 字符串時,寫入文件,否則僅在控制枱輸出
{ FF輸出字符串 = ( 字符串1 , 字符串2 ) => FF寫入文件 ( 字符串1 , 字符串2 ); }
else
{ FF輸出字符串 = ( 字符串1 , 字符串2 ) => FF寫入控制枱 ( 字符串1 , 字符串2 ); }
FF輸出字符串 ( zfc1 , zfc2 );
static void FF寫入控制枱 ( string 字符串1 , string 字符串2 )
{
Console . WriteLine ( $"{字符串1}\n{字符串2}" );
}
static void FF寫入文件 ( string 字符串1 , string 字符串2 )
{
using StreamWriter sw = new ( @"F:\測試文件夾\Action.txt" );
{
try { sw . WriteLine ( $"{字符串1}\n{字符串2}" ); }
catch ( Exception e ) { Console . WriteLine ( e . ToString ( ) ); }
}
}
using System . Linq;
Func < string? , int , bool > FF取決於 = ( 字符串 , 索引 ) => ( 字符串? . Length == 2 ) && ( 索引 % 2 == 0 ) && ( 字符串! . All ( char . IsLetter ) );
string? [ ] ZFCs兵器 = [ "步槍" , "輕機槍" , "驅逐艦" , "炮艇" , "坦克" , "美女" , "" , null ];
IEnumerable < string? > bq = ZFCs兵器 . Where ( FF取決於 );
foreach ( var b in bq )
Console . WriteLine ( b );
string [ ] ZFCs源 = [ "狼" , "豬" , "兔子" , "野雞" , "野驢" ];
string [ ] ZFCs目標 = new string [ ZFCs源 . Length ];
// 直接使用方法名賦值,無需 Lambda 包裝
Action < string [ ] , string [ ] , int > FF複製字符串方法 = FF複製字符串;
// 通過委託調用方法
FF複製字符串方法 ( ZFCs源 , ZFCs目標 , 3 );
// 輸出結果
foreach ( string z in ZFCs目標 )
Console . WriteLine ( string . IsNullOrEmpty ( z ) ? "<無>" : z );
static void FF複製字符串 ( string [ ] 源 , string [ ] 目標 , int 起始索引 )
{
// 參數驗證
ArgumentNullException . ThrowIfNull ( 源 );
ArgumentNullException . ThrowIfNull ( 目標 );
if ( 源 . Length != 目標 . Length )
throw new ArgumentException ( "源數組和目標數組必須具有相同的元素數。" );
if ( 起始索引 > 源 . Length || 起始索引 < 0 )
throw new ArgumentOutOfRangeException ( nameof ( 起始索引 ) , "起始索引不在源數組有效範圍內。" );
// 執行復制
for ( int z = 起始索引 ; z < 源 . Length ; z++ )
{
目標 [ z ] = 源 [ z ];
}
}
string zfcShuZhi = "-1,234";
Func < string , NumberStyles , IFormatProvider , int > ZhuanHuanQi區域 = int . Parse;
Func < string , NumberStyles , int > ZhuanHuanQi無區域 = int . Parse;
Func < string , int > ZhuanHuanQi無樣式 = int . Parse;
Console . WriteLine ( ZhuanHuanQi區域 ( zfcShuZhi , NumberStyles . Integer | NumberStyles . AllowThousands , CultureInfo . InvariantCulture ) );
Console . WriteLine ( ZhuanHuanQi無區域 ( zfcShuZhi , NumberStyles . Integer | NumberStyles . AllowThousands ) );
try
{
Console . WriteLine ( ZhuanHuanQi無樣式 ( zfcShuZhi ) );
}
catch ( Exception yc ) { Console . WriteLine ( yc . ToString ( ) ); }
string [ ] ZFCs源 = ["狼", "豬", "兔子", "野雞", "野驢"];
string [ ] ZFCs目標 = new string[ ZFCs源 . Length ];
// 直接使用方法名賦值,無需 Lambda 包裝
Action < string [ ] , string [ ] , int > FF複製字符串方法 = ( 源 , 目標 , 起始索引 ) => FF複製字符串 ( 源 , 目標 , 起始索引 );
Action < string [ ] , string [ ] , int , int > FF複製字符串方法2 = ( 源 , 目標 , 起始索引 , 元素數 ) => FF複製字符串 ( 源 , 目標 , 起始索引 , 元素數 );
// 通過委託調用方法
FF複製字符串方法 ( ZFCs源 , ZFCs目標 , 3 );
// 輸出結果
foreach ( string z in ZFCs目標 )
Console . WriteLine ( string . IsNullOrEmpty ( z ) ? "<無>" : z );
Array . Clear ( ZFCs目標 );
Console . WriteLine ( "\n複製 2 個元素:" );
// 通過委託調用方法
FF複製字符串方法2 ( ZFCs源 , ZFCs目標 , 2 , 2 );
// 輸出結果
foreach ( string z in ZFCs目標 )
Console . WriteLine ( string . IsNullOrEmpty ( z ) ? "<無>" : z );
Array . Clear ( ZFCs目標 );
Console . WriteLine ( "\n複製 0 個元素:" );
// 通過委託調用方法
FF複製字符串方法2 ( ZFCs源 , ZFCs目標 , 2 , 0 );
// 輸出結果
foreach ( string z in ZFCs目標 )
Console . WriteLine ( string . IsNullOrEmpty ( z ) ? "<無>" : z );
static void FF複製字符串 ( string [ ] 源 , string [ ] 目標 , int 起始索引 , int? 元素數 = null )
{
// 參數驗證
ArgumentNullException . ThrowIfNull ( 源 );
ArgumentNullException . ThrowIfNull ( 目標 );
if ( 源 . Length != 目標 . Length )
throw new ArgumentException ( "源數組和目標數組必須具有相同的元素數。" );
if ( 起始索引 > 源 . Length || 起始索引 < 0 || 元素數 < 0 || 起始索引 + 元素數 > 源 . Length )
throw new ArgumentOutOfRangeException ( nameof ( 起始索引 ) , "起始索引 或 起始索引 + 元素數 不在源數組有效範圍內。" );
switch ( 元素數 == null )
{
case ( true ):
元素數 = 源 . Length - 起始索引;
break;
case ( false ):
元素數 = Math . Min ( 源 . Length - 起始索引 , 元素數 . Value );
break;
}
// 執行復制
for ( int z = 0 ; z < 元素數 ; z++ )
{
int 索引 = z + 起始索引;
目標 [ 索引 ] = 源 [ 索引 ];
}
}
string zfc書名 = "The House of the Seven Gables";
int zhs位置 = 0;
Func < string , int , int , StringComparison , int > SouSuo = ( 搜索源 , 位置 , 字符數 , 搜索類型 ) => zfc書名 . IndexOf ( 搜索源 , 位置 , 字符數 , 搜索類型 );
do
{
int zhs字符數 = zfc書名 . Length - zhs位置;
zhs位置 = SouSuo ( "the" , zhs位置 , zhs字符數 , StringComparison . InvariantCultureIgnoreCase );
if ( zhs位置 >= 0 )
{
zhs位置++;
Console . WriteLine ( $"在 {zfc書名} 的位置 {zhs位置} 找到 ‘The’。" );
}
} while ( zhs位置 > 0 );