參數修飾詞 in、out 和 ref 之間的差異

 

雖然對比於其他關鍵字,out 和 ref 關鍵字會較不熟悉,

不過還是來比較一下兩者的不同。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string a;
            testOut(out a);

            string b = "ccc";
            testRef(ref b);

            Console.WriteLine("string a = {0}, string b = {1}", a, b);

            int readonlyArgument = 44;
            InArgExample(readonlyArgument);
            Console.WriteLine(readonlyArgument);     // value is still 44

            Console.ReadKey();
        }

        static void testOut(out string i)
        {
            i = "ggg";
        }

        static void testRef(ref string i)
        {
            //can be do nothing
        }

        static void InArgExample(in int number)
        {
            // Uncomment the following line to see error CS8331
            //number = 19;
        }
    }
}

1、在呼叫使用 ref 宣告輸入參數的方法時,呼叫端的輸入參數需先初始化;

反而使用 out 宣告輸入參數的方法,呼叫端的輸入參數不需要先初始化。

2、使用 ref 宣告輸入參數的方法時,輸入參數允許什麼事都不用做;

反而使用 out 宣告輸入參數的方法時,在方法回傳前輸入參數一定要被指派值。

3、編譯器在多載方法上,並不會把參數修飾詞視為不同的簽章 (signature),

也就是說,一種方法採用 ref 引數,而另一種方法採用 out 引數,

只要 ref、in、out 三者同時存在兩個,則不能多載方法。

class CS0663_Example
{
    // Compiler error CS0663: "Cannot define overloaded
    public void SampleMethod(ref int i) { }
    public void SampleMethod(out int i) { i = 1; }
    public void SampleMethod(in int i) { }
}

4、必須在 C# 7.2 以上能使用 in 參數修飾詞,in 參數修飾詞是以傳址方式傳遞引數,

你可以先把 in 宣告的特性想成就是等於 ref 宣告,

但與 ref 宣告不同的是,經由 in 宣告的值是不能被修改的。

5、ref 與 out 在使用上,其 method argument 與 method parameter 前面都要明確指定,

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string a;
            testOut(out a);

            string b = "ccc";
            testRef(ref b);
        }

        static void testOut(out string i) { i = "ggg"; }

        static void testRef(ref string i) { }
    }
}

而 in 在使用上則不須在 method argument 前面指定 in 參數修飾詞

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            InArgExample(44);
        }

        static void InArgExample(in int number) { }
    }
}

6、在 C# 7.2,有加「in」參數修飾詞與沒有加修飾詞的方法,兩者可以多載,

當想要使用帶有「in」參數修飾詞的方法時,

只要在呼叫時也加上「in」參數修飾詞,就可區分開來。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Show(3);

            int readonlyArgument = 100;
            Show(in readonlyArgument);
        }

        static void Show(int arg)
        {
            Console.WriteLine(arg);
        }

        static void Show(in int arg)
        {
            Console.WriteLine("in - " + arg);
        }
    }
}

執行結果為

 

7、out

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Call();
            /*below the commented code,can not to be executed*/
            //Console.WriteLine($"({x}, {y})");
            Console.ReadKey();
        }

        static void Call()
        {
            GetCoordinates(out int x, out int y);
            Console.WriteLine($"({x}, {y})");
        }

        static void GetCoordinates(out int a, out int b)
        {
            a = 100;
            b = 200;
        }
    }
}

說明:

a、現在參數修飾詞「out」也可以在傳遞時再宣告,這可以減少程式碼的行數,增加可讀性。

b、這個變數在包含它本身的 { } 封閉區塊範圍內,所以接續宣告後面的程式碼可以直接使用這些變數。

c、如果你不在意某個 out 參數的傳回結果,可以使用 _ 代表忽略它:

static void Call()
{
    GetCoordinates(out int x, out _);
    Console.WriteLine($"({x})");
}

 

參考資料:

使用 ref 和 out 傳遞陣列 (C# 程式設計手冊)

out (C# 參考)

ref (C# 參考)

in parameter modifier (C# Reference)