亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C#可變參數(shù)params示例詳解

 更新時間:2022年02月08日 14:15:09   作者:yi念之間  
params是c#的一個關鍵字,用用漢語來說的話叫可變參數(shù),這里的可變,不是說的類型可變,而是指的個數(shù)可變,這是c#的一個基礎關鍵字,相信大家都有一定的了解,本篇文章就來大致的說一下C#可變參數(shù)params

前言

前幾天在群里看到群友寫了一個基礎框架,其中設計到關于同一個詞語可以添加多個近義詞的一個場景。當時群友的設計是類似字典的設計,直接添加k-v的操作,本人看到后思考了一下覺得使用c#中的params可以更優(yōu)雅的實現(xiàn)一個key同時添加一個集合的操作,看起來會更優(yōu)雅一點,這期間還有群友說道params和數(shù)組有啥區(qū)別的問題。本篇文章就來大致的說一下。

示例

params是c#的一個關鍵字,用用漢語來說的話叫可變參數(shù),這里的可變,不是說的類型可變,而是指的個數(shù)可變,這是c#的一個基礎關鍵字,相信大家都有一定的了解,今天咱們就來進一步看一下c#的可變參數(shù)params。首先來看一下簡單的自定義使用,隨便定義一個方法

static void ParamtesDemo(string className, params string[] names)
{
    Console.WriteLine($"{className}的學生有:{string.Join(",", names)}");
}

定義可變參數(shù)類型的時候需要有幾個注意點

  • params修飾在參數(shù)的前面且參數(shù)類型得是一維數(shù)組類型
  • params修飾的參數(shù)默認是可以不傳遞的
  • params參數(shù)不能用ref或out修飾且不能手動給默認值

調(diào)用的時候更簡單了,如下所示

ParamtesDemo("小四班", "jordan", "kobe", "james", "curry");
// 如果不傳遞值也不會報錯
// ParamtesDemo("小四班");

由上面的示例可知,使用可變參數(shù)最大的優(yōu)勢就是你可以傳遞一個不確定個數(shù)的集合類型并且不用聲明單獨的類型去包裝,這種場景特別適合傳遞參數(shù)不確定的場景,比如我們經(jīng)常使用到的string.Format就是使用的可變參數(shù)類型。

探究本質(zhì)

通過上面我們了解到的params的遍歷性,當集合參數(shù)個數(shù)不確定的時候是使用可變參數(shù)的最佳場景,看著很神奇很便捷,本質(zhì)到底是什么呢?之前樓主也沒有在意這個問題,直到前幾天懷揣著好奇的心情看了一下。廢話不多說,我們直接借助ILSpy工具看一下反編譯之后的源碼

[CompilerGenerated]
internal class Program
{
	private static void <Main>$(string[] args)
	{
        //聲明了一個數(shù)組
		ParamtesDemo("小四班", new string[4] { "jordan", "kobe", "james", "curry" });
        Console.ReadKey();

        //已經(jīng)沒有params關鍵字了,就是一個數(shù)組
		static void ParamtesDemo(string className, string[] names)
		{
			Console.WriteLine(className + "的學生有:" + string.Join(",", names));
		}
	}
}

通過ILSpy反編譯的源碼我們可以看到params是一個語法糖,其實就是增加了編程效率,本質(zhì)在編譯的時候會被具體的聲明的數(shù)組類型替代,不參與到運行時。這個時候如果你懷疑反編譯的代碼有問題,可以直接通過ILSpy看生成的IL代碼,由于IL代碼比較長,首先看一下Main方法

// Methods
.method private hidebysig static 
		void '<Main>$' (
			string[] args
		) cil managed 
{
	// Method begins at RVA 0x2092
	// Header size: 1
	// Code size: 57 (0x39)
	.maxstack 8
	.entrypoint

	// ParamtesDemo("小四班", new string[4] { "jordan", "kobe", "james", "curry" });
	IL_0000: ldstr "小四班"
	IL_0005: ldc.i4.4
        //通過newarr可知確實是聲明了一個數(shù)組類型
	IL_0006: newarr [System.Runtime]System.String
	IL_000b: dup
	IL_000c: ldc.i4.0
	IL_000d: ldstr "jordan"
	IL_0012: stelem.ref
	IL_0013: dup
	IL_0014: ldc.i4.1
	IL_0015: ldstr "kobe"
	IL_001a: stelem.ref
	IL_001b: dup
	IL_001c: ldc.i4.2
	IL_001d: ldstr "james"
	IL_0022: stelem.ref
	IL_0023: dup
	IL_0024: ldc.i4.3
	IL_0025: ldstr "curry"
	IL_002a: stelem.ref
	// 這個地方調(diào)用了ParamtesDemo,第二個參數(shù)確實是一個數(shù)組類型
	IL_002b: call void Program::'<<Main>$>g__ParamtesDemo|0_0'(string, string[])
	// Console.ReadKey();
	IL_0030: nop
	IL_0031: call valuetype [System.Console]System.ConsoleKeyInfo [System.Console]System.Console::ReadKey()
	IL_0036: pop
	// }
	IL_0037: nop
	IL_0038: ret
} // end of method Program::'<Main>$'	

通過上面的IL代碼可以看到確實是一個語法糖,編譯完之后一切塵歸塵土歸土還是一個數(shù)組類型,類型是和params修飾的那個數(shù)組類型是一致的。接下來我們再來看一下ParamtesDemo這個方法的IL代碼是啥樣的

//names也是一個數(shù)組
.method assembly hidebysig static 
	void '<<Main>$>g__ParamtesDemo|0_0' (
		string className,
		string[] names
	) cil managed 
{
	.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
		01 00 01 00 00
	)
	.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
		01 00 00 00
	// Method begins at RVA 0x20d5
	// Header size: 1
	// Code size: 30 (0x1e)
	.maxstack 8

	// {
	IL_0000: nop
	// Console.WriteLine(className + "的學生有:" + string.Join(",", names));
	IL_0001: ldarg.0
	IL_0002: ldstr "的學生有:"
	IL_0007: ldstr ","
	IL_000c: ldarg.1
	IL_000d: call string [System.Runtime]System.String::Join(string, string[])
	IL_0012: call string [System.Runtime]System.String::Concat(string, string, string)
	IL_0017: call void [System.Console]System.Console::WriteLine(string)
	// }
	IL_001c: nop
	IL_001d: ret
} // end of method Program::'<<Main>$>g__ParamtesDemo|0_0'

一切了然,本質(zhì)就是那個數(shù)組。我們上面還提到了params修飾的參數(shù)默認不傳遞的話也不會報錯,這究竟是為什么呢,我們就用IL代碼來看一下究竟進行了何等操作吧

// Methods
.method private hidebysig static 
	void '<Main>$' (
		string[] args
	) cil managed 
{
	// Method begins at RVA 0x2092
	// Header size: 1
	// Code size: 24 (0x18)
	.maxstack 8
	.entrypoint

	// ParamtesDemo("小四班", Array.Empty<string>());
	IL_0000: ldstr "小四班"
        // 本質(zhì)是編譯的時候幫我們聲明了一個空數(shù)組Array::Empty<string>
	IL_0005: call !!0[] [System.Runtime]System.Array::Empty<string>()
	IL_000a: call void Program::'<<Main>$>g__ParamtesDemo|0_0'(string, string[])
	// Console.ReadKey();
	IL_000f: nop
	IL_0010: call valuetype [System.Console]System.ConsoleKeyInfo [System.Console]System.Console::ReadKey()
	IL_0015: pop
	// }
	IL_0016: nop
	IL_0017: ret
} // end of method Program::'<Main>$'

原來這得感謝編譯器,如果默認不傳遞params修飾的參數(shù)的話,默認它會幫我們生成一個這個類型的空數(shù)組,這里需要注意的不是null,所以代碼不會報錯,只是沒有數(shù)據(jù)。

擴展知識

我們上面提到了string.Format也是基于params實現(xiàn)的,畢竟Format具體的參數(shù)依賴于前面聲明的字符串的占位符個數(shù)。在翻看相關代碼的時候還發(fā)現(xiàn)了一個ParamsArray這個類,用來包裝params可變參數(shù),簡單的來說就是便于快速操作params,這個我是在Format方法中發(fā)現(xiàn)的,源代碼如下

public static string Format(string format, params object?[] args)
{
    if (args == null)
    {
        throw new ArgumentNullException((format == null) ? nameof(format) : nameof(args));
    }
    return FormatHelper(null, format, new ParamsArray(args));
}

params參數(shù)也可以為null值,默認不會報錯,但是需要進行判斷,否則程序處理null可能會報錯。在這里我們可以看到把params參數(shù)傳遞給ParamsArray進行包裝,我們可以看一下ParamsArray類本身的定義,這個類是一個struct類型的

internal readonly struct ParamsArray
{
    //定義是三個數(shù)組分別去承載當傳遞進來的params不同個數(shù)時的數(shù)據(jù)
    private static readonly object?[] s_oneArgArray = new object?[1];
    private static readonly object?[] s_twoArgArray = new object?[2];
    private static readonly object?[] s_threeArgArray = new object?[3];

    //定義三個值分別存儲params的第0、1、2個參數(shù)的值
    private readonly object? _arg0;
    private readonly object? _arg1;
    private readonly object? _arg2;
    //承載最原始的params值
    private readonly object?[] _args;
    //params值為1個的時候
    public ParamsArray(object? arg0)
    {
        _arg0 = arg0;
        _arg1 = null;
        _arg2 = null;
        _args = s_oneArgArray;
    }
    //params值為2個的時候
    public ParamsArray(object? arg0, object? arg1)
        _arg1 = arg1;
        _args = s_twoArgArray;
    //params值為3個的時候
    public ParamsArray(object? arg0, object? arg1, object? arg2)
        _arg2 = arg2;
        _args = s_threeArgArray;
    //直接包裝整個params的值
    public ParamsArray(object?[] args)
        //直接取出來值緩存
        int len = args.Length;
        _arg0 = len > 0 ? args[0] : null;
        _arg1 = len > 1 ? args[1] : null;
        _arg2 = len > 2 ? args[2] : null;
        _args = args;
    public int Length => _args.Length;
    public object? this[int index] => index == 0 ? _arg0 : GetAtSlow(index);
    //判斷是否從承載的緩存中取值
    private object? GetAtSlow(int index)
        if (index == 1)
            return _arg1;
        if (index == 2)
            return _arg2;
        return _args[index];
}

ParamsArray是一個值類型,目的就是為了把params參數(shù)的值給包裝起來提供讀相關的操作。根據(jù)二八法則來看,params大部分場景的參數(shù)個數(shù)或者高頻訪問可能是存在于數(shù)組的前幾位元素上,所以使用ParamsArray針對熱點元素提供了快速訪問的方式,略微有一點像Java中的IntegerCache的設計。這個結(jié)構體是internal類型的,默認程序集之外是沒辦法訪問的,我當時看到的時候比較好奇,就多看了一眼,感覺設計思路還是考慮的比較周到的。

總結(jié)

本文主要簡單的聊一下c#可變參數(shù)params的本質(zhì),了解到了其實就是一個語法糖,編譯完成之后本質(zhì)還是一個數(shù)組。它的好處就是當我們不確定集合個數(shù)的時候,可以靈活的使用params進行參數(shù)傳遞,不用自行定義一個集合類型。然后微軟針對params在內(nèi)部實現(xiàn)了一個ParamsArray結(jié)構體進行對params包裝,提升params類型的訪問。
新年伊始,聊一點個人針對學習的看法。學習最理想的結(jié)果就是把接觸到的知識進行一定的抽象,轉(zhuǎn)換為概念或者一種思維方式,然后細化這種思維,讓它成為細顆粒度的知識點,然后我們通過不斷的接觸不斷的積累,后者不同領域的接觸等,不斷吸收壯大這個思維庫。然后當看到一個新的問題的時候,或者需要思考的時候,能達到快速的多角度的整合這些思維碎片,得到一個更好的思路或解決問題的辦法,這也許是一種更行之有效的狀態(tài)。類比到我們架構設計上來說,以前的思維方式是一種類似單體應用的方式,靈活性差擴展性更差,后來微服務概念大行其道,更多獨立的服務相互協(xié)調(diào)工作,形成一種更強大的聚合力。

到此這篇關于C#可變參數(shù)params示例詳解的文章就介紹到這了,更多相關C#可變參數(shù)params內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C#實現(xiàn)客戶端彈出消息框封裝類實例

    C#實現(xiàn)客戶端彈出消息框封裝類實例

    這篇文章主要介紹了C#實現(xiàn)客戶端彈出消息框封裝類,實例分析了C#彈出窗口的實現(xiàn)技巧,非常具有實用價值,需要的朋友可以參考下
    2015-03-03
  • C#引用類型和值類型的適用場合和區(qū)別

    C#引用類型和值類型的適用場合和區(qū)別

    今天小編就為大家分享一篇關于C#引用類型和值類型的適用場合和區(qū)別,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • C#編程實現(xiàn)簡易圖片瀏覽器的方法

    C#編程實現(xiàn)簡易圖片瀏覽器的方法

    這篇文章主要介紹了C#編程實現(xiàn)簡易圖片瀏覽器的方法,涉及C#基于WinForm操作圖片實現(xiàn)預覽功能的相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-11-11
  • c#中設置快捷鍵

    c#中設置快捷鍵

    c#中設置快捷鍵...
    2007-03-03
  • C#實現(xiàn)快速排序算法

    C#實現(xiàn)快速排序算法

    本文詳細講解了C#實現(xiàn)快速排序算法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • unity實現(xiàn)繪畫功能

    unity實現(xiàn)繪畫功能

    這篇文章主要為大家詳細介紹了unity實現(xiàn)繪畫功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • C#實現(xiàn)為視頻添加水印

    C#實現(xiàn)為視頻添加水印

    這篇文章主要為大家詳細介紹了C#如何使用ffmpeg命令,分別實現(xiàn)給視頻添加圖片水印以及文字水印,文中的示例代講解詳細,感興趣的可以了解一下
    2023-01-01
  • LINQ基礎之From和GroupBy子句

    LINQ基礎之From和GroupBy子句

    這篇文章介紹了LINQ使用From和GroupBy子句的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • c#通過進程調(diào)用cmd判斷登錄用戶權限代碼分享

    c#通過進程調(diào)用cmd判斷登錄用戶權限代碼分享

    最近自己開發(fā)軟件需要讀取本地配置文件,因為登錄用戶的權限不夠會導致無法讀取文件進而導致程序崩潰,查了一些解決方法,代碼分享如下
    2013-12-12
  • 用C#在本地創(chuàng)建一個Windows帳戶(DOS命令)

    用C#在本地創(chuàng)建一個Windows帳戶(DOS命令)

    用C#在本地創(chuàng)建一個Windows帳戶(DOS命令)...
    2007-03-03

最新評論