您似乎誤解了C#中參數傳遞的方式。默認情況下,所有參數均以C#中的值傳遞。爲了通過參考來傳遞它們,您需要使用特殊關鍵字:ref
或out
當參數用作給定方法的第二個輸出時,通常使用後者。 (例如見int.TryPase
)。
這裏要理解的重要一點是,如果參數的類型是引用類型或值類型,那麼通過值傳遞的參數表現得相當不同。這是你似乎困惑的地方。
理解它是如何工作的,請確保您明確以下幾點:
- 變量持有值。
的值,其類型是值類型變量,是該值類型的實例本身:
int i = 1 // i holds the value 1
的值,其類型的變量的是參考類型不是所述類型的實例。值爲該實例所在的內存地址。
string s = "Hello!" // s does not hold "Hello!" it holds a number that points to a place in memory where the string "Hello!" lives.
所以,現在我們都清楚這一點,當你按值傳遞參數(C#的默認值)會發生什麼?會發生什麼是變量的副本,副本傳遞給方法。
如果類型是引用類型,那麼真正複製並傳遞給方法的是存儲在變量中的值。那是什麼?變量引用的對象所在的內存地址。所以你看看會發生什麼?兩個原始變量和複製的可變都指向同一個對象:
public class Foo
{
var frobbed = false;
public bool Frobbed { get { return frobbed; } }
public void Frob() { frobbed = true; }
}
void Frob(Foo foo) { foo.Frob(); }
var myFoo = new Foo();
Frob(myFoo);
Console.WriteLine(myFoo.Frobbed); //Outputs True! Why? Because myFoo and foo both point to the same object! The value of both variables (memory address) is the same!
如果類型爲值類型,值類型的值本身被拷貝並送交方法,所以沒有方法可以修改存儲在原始變量中的值類型。
public void Increment(int i) { i = i + 1; }
var myInt = 1;
Increment(myInt);
Console.WriteLine(myInt); //Outputs 1, not 2. Why? i holds its own copy of 1, it knows nothing about the copy of 1 stored in myInt.
當您通過引用傳遞參數時,事情會改變。現在,傳遞給該方法的參數不是副本,而是其原始變量本身。一個邏輯問題如下:這實際上是否改變了參考類型的行爲方式?答案是肯定的,相當多:
public void ByValueCall(string s)
{
s = "Goodbye";
}
public void ByReferenceCall(ref string s)
{
s = "Goodbye";
}
var myString = "Hello!";
Console.WriteLine(ByValueCall(myString)); //outputs "Hello!"
Console.WriteLine(ByValueCall(myString)); //outputs "Goodbye!"
此行爲與值類型也相同。這裏發生了什麼?
當您通過值傳遞參數時,該方法獲取變量的副本;因此爲該參數分配一個新值實際上只是爲該副本分配一個新值;在調用網站的原始變量不關心你改變它的拷貝的值,不管它是一個值類型還是一個引用類型。它將繼續保持它始終擁有的價值。
當通過引用傳遞參數時,您不傳遞副本,而是傳遞變量本身。在這種情況下,爲變量分配一個新的值將保留在調用點。一個典型的例子就是下面的交換方法:
public void Swap(ref int a, ref int b)
{
var temp = a;
a = b;
b = temp;
}
var i1 = 1;
var i2 = 2;
Swap(ref i1, ref i2);
var b = i1 == 2; //true
b = i2 == 1; //true
所以經過這一切,你應該明白爲什麼下面的行爲方式是這樣:
public ResetStream(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
}
var myStream = new ...
myStream.Read(bytes, 0, 1024);
ResetStream(myStream);
var isAtOrigin = myStream.Position == 0; //Returns true!
是。流是訪問方法,而不是內容。而且無論如何你都無法通過它來傳遞它,它是一個引用類型 –
@PanagiotisKanavos這是否意味着我可以在ValidateStreamLine方法中進行重置,它也可以工作? –
@PanagiotisKanavos我很挑剔,但它是通過值傳遞的,它是一個通過值傳遞的引用類型 –