C#中Ref/Out/In關(guān)鍵字解析

前陣子我也不知道在看哪邊的代碼,突然發(fā)現(xiàn)有個(gè)關(guān)鍵字in,我在想C#什么時(shí)候多出來(lái)了這個(gè)關(guān)鍵字。一查之下,原來(lái)是C#7.2的新特性,難怪以前從來(lái)沒(méi)見(jiàn)過(guò)呢。那么這個(gè)新特性有什么用呢?查來(lái)查去,發(fā)現(xiàn)這東西和ref/out關(guān)鍵字很類(lèi)似,所以這篇博客干脆將他們放在一起進(jìn)行記錄。

首先我們都知道C#中有引用類(lèi)型和值類(lèi)型,引用類(lèi)型包含了一個(gè)數(shù)據(jù)存儲(chǔ)在內(nèi)存中的引用,其在堆內(nèi)存(heap)中,生命周期比較長(zhǎng),并且可以有多個(gè)變量指向同一個(gè)引用,對(duì)象即是此種類(lèi)型的典型。而值類(lèi)型包含的是數(shù)據(jù)本身而并非引用,其生命周期比較短,通常存放在棧內(nèi)存(stack)中,Int32、Struct、Double這些都是值類(lèi)型中的代表。

一個(gè)方法在傳入的parameter為引用類(lèi)型時(shí),傳入的是引用的一份拷貝,而不是parameter的真實(shí)數(shù)據(jù)。如果你在方法內(nèi)改變了parameter的數(shù)據(jù),那么外部的數(shù)據(jù)也會(huì)被改變。然而,當(dāng)你在方法內(nèi)部為parameter賦值一個(gè)新的對(duì)象時(shí),你并沒(méi)有改變外部的數(shù)據(jù),而是在改變方法內(nèi)的本地?cái)?shù)據(jù)而已??聪旅娴拇a就知道我在說(shuō)什么了:

void Start()
    {
        Colleague col = new Colleague();
        CreateColleague(col);
        Debug.Log(col.Name + " " + col.Sex);
    }

public void CreateColleague(Colleague c){
        c.Name = "Mike";
        c.Sex = "male";
        c = new Colleague();//這里不會(huì)改變外部的col變量,所以外面打印出來(lái)的結(jié)果是Mike和male
        c.Name = "Nacy";
        c.Sex = "female";
    }

public class Colleague{
        private string name;
        public string Name{
            get{
                return name;
            }
            set{
                name = value;
            }
        }
        private string sex;
        public string Sex{
            get{
                return sex;
            }
            set{
                sex = value;
            }
        }
        public Colleague(){

        }


    }

那么,可不可以在方法中賦值一個(gè)新的對(duì)象并且將外部的變量改變了呢?答案是肯定的,加個(gè)ref關(guān)鍵字就行了,不過(guò)謹(jǐn)記要先初始化好。如下代碼:

void Start()
    {
        Colleague col = new Colleague();
        CreateRefColleague(ref col);
        Debug.Log(col.Name + " " + col.Sex);//這里打印出來(lái)的結(jié)果就是Nacy和female了

    }

public void CreateRefColleague(ref Colleague c){
        c.Name = "Mike";
        c.Sex = "male";
        c = new Colleague();
        c.Name = "Nacy";
        c.Sex = "female";
    }

public class Colleague{
        private string name;
        public string Name{
            get{
                return name;
            }
            set{
                name = value;
            }
        }
        private string sex;
        public string Sex{
            get{
                return sex;
            }
            set{
                sex = value;
            }
        }
        public Colleague(){

        }


    }

并且,ref關(guān)鍵字也適用于值類(lèi)型的parameter。如下代碼所示:

void Start()
    {
       

        int n = 1;
        IncrementInt(ref n);
        Debug.Log(n);//n變成了2

    }

public void IncrementInt(ref int n){
        n++;
    }

out關(guān)鍵字與ref的使用很相似,不過(guò)通常來(lái)講out關(guān)鍵字修飾的變量不先進(jìn)行初始化,而是在方法中初始化它。

void Start()
    {
        
        Colleague co;//不初始化變量
        CreateOutColleague(out co);//將變量放進(jìn)方法中初始化
        Debug.Log(co.Name + " " + co.Sex);

    }

public void CreateOutColleague(out Colleague c){
        c = new Colleague();
        c.Name = "Mike";
        c.Sex = "male";
        
    }

    public class Colleague{
        private string name;
        public string Name{
            get{
                return name;
            }
            set{
                name = value;
            }
        }
        private string sex;
        public string Sex{
            get{
                return sex;
            }
            set{
                sex = value;
            }
        }
        public Colleague(){

        }


    }

而in關(guān)鍵字就很有意思了,它也像ref一樣需要初始化好,但是它的用處是不讓方法內(nèi)部對(duì)其進(jìn)行賦值新的對(duì)象,如下代碼:

void Start()
    {
        Colleague col = new Colleague();
        CreateInColleague(col);
        Debug.Log(col.Name + " " + col.Sex);
    }

public void CreateInColleague(in Colleague c){
        //c = new Colleague();//compile error
        c.Name = "Mike";
        c.Sex = "male";
        
    }

 public class Colleague{
        private string name;
        public string Name{
            get{
                return name;
            }
            set{
                name = value;
            }
        }
        private string sex;
        public string Sex{
            get{
                return sex;
            }
            set{
                sex = value;
            }
        }
        public Colleague(){

        }


    }

要是在方法內(nèi)執(zhí)行c = new Colleague();的話就會(huì)拋出如下錯(cuò)誤

編譯錯(cuò)誤

那么這個(gè)新特性in關(guān)鍵字除了這個(gè)用處還有什么用呢?我查閱了一些資料發(fā)現(xiàn),當(dāng)下似乎只有一個(gè)用處,那就是這樣

readonly struct VeryLarge
{
    public readonly long Value1;   
    public readonly long Value2;

    public long Compute() { }
    // etc
}

void Process(in VeryLarge value) { }

當(dāng)你有一個(gè)很大的結(jié)構(gòu)體并且這個(gè)結(jié)構(gòu)體是readonly的時(shí)候(readonly結(jié)構(gòu)體也是C#7.2的新特性),在方法中使用in關(guān)鍵字可以使得這個(gè)方法被高效的執(zhí)行,因?yàn)槠浞乐沽薲efensive copy的發(fā)生。而如果使用了in關(guān)鍵字卻沒(méi)有讓結(jié)構(gòu)體是readonly的情況下,defensive copy將會(huì)發(fā)生,影響性能!

這里的這個(gè)defensive copy是什么意思呢?原來(lái),編譯器為了防止任何潛在的在方法內(nèi)改變readonly變量(像這里的readonly long Value1/Value2)的可能,會(huì)有一個(gè)防御性的拷貝,將原來(lái)的readonly變量拷貝出來(lái),對(duì)這個(gè)變量操作幾次就拷貝幾次,可想而知這對(duì)性能會(huì)產(chǎn)生多大影響。而有了in關(guān)鍵字,這個(gè)問(wèn)題就被解決了!

當(dāng)然,這三個(gè)關(guān)鍵字也有不能使用的時(shí)候,一種情況是async修飾的方法里不能用,你可以繞個(gè)彎,在同步方法內(nèi)返回一個(gè)Task,這樣的方法內(nèi)是可以用的。另一種情況是迭代器方法(iterator method)中有yield return或者yield break的情況下不能用。

在overloading時(shí),有上述任何關(guān)鍵字方法的簽名都會(huì)和沒(méi)有上述關(guān)鍵字方法的簽名不同,而如果只是關(guān)鍵字的不同,編譯器會(huì)報(bào)錯(cuò)。

編譯報(bào)錯(cuò)

最后總結(jié):

  1. ref是表明parameter可能會(huì)被方法所改變,需要初始化。
  2. out是表明parameter一定會(huì)被方法所改變,不需要初始化。
  3. in是表明parameter不能被方法所改變,需要初始化。

項(xiàng)目地址

參考
C# - Using in, out, and Ref with Parameters
C# - Passing a Reference vs. Value
Why would one ever use the “in” parameter modifier in C#?
The ‘in’-modifier and the readonly structs in C#

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容