2012-04-25 100 views
4

我可以使用遞歸方法使用out參數嗎?如果可能的話,我怎麼用下面的代碼來做到這一點?在C#中使用「out參數」遞歸

private void PrepareDir(out List<_File> listFiles,string root,string dirPath) { 
    DirectoryInfo dirRoot = new DirectoryInfo(dirPath); 
    FileInfo [] Files = dirRoot.GetFiles(); 
    dirPath = dirPath.Substring(root.Length); 

    foreach (FileInfo file in Files) { 
     _File _file = new _File(); 
     _file.Name = dirPath + "\\" + file.Name; 
     _file.Path = file.FullName; 
     _file.Size = file.Length; 
     listFiles.Add(_file); 
    } 

    foreach (DirectoryInfo dir in dirRoot.GetDirectories()) { 
     PrepareDir(out listFiles, root, dir.FullName); 
    } 
} 

private void btnButton1_Click(object sender, EventArgs e) { 
    List<_File> Files = new List<_File>(); 
    PrepareDir(out Files,currAddress, currAddress); 
} 
+6

這不應該是'out'參數。 – SLaks 2012-04-25 03:08:42

+1

考慮到你的listFiles參數是通過引用傳遞的(因爲它是一個引用類型),你實際上並沒有重新分配標識符; ergo:'out'沒有必要,代碼可以在你的情況下沒有它。 – payo 2012-04-25 03:09:32

+0

@SLaks,所以不可能使用遞歸方法? – 2012-04-25 03:10:09

回答

5

我決定改寫我的答案,以便更直接地說明爲什麼在這裏不需要使用out。我寫這篇文章的時間比較冗長,因爲我認爲你的問題的根源在於不理解價值傳遞,參照傳遞和參考類型通過參考傳遞之間的共同混淆之間的差異。還要注意,這不是隻顯示遞歸。

在C#中,引用類型默認按值傳遞。很多人可能會將引用類型與引用傳遞混淆,但有一個重要的區別。爲了讓自己更清楚,我會參考一個引用類型,並通過引用的方式使用refout關鍵字。

按值傳遞引用類型,因爲它是對內存中某個存儲位置的引用,允許您創建並保留更改。以這個例子。

public class MyRefType 
{ 
    public int Value { get; set; } 
} 

public void Foo() { 
    MyRefType type = new MyRefType(); 
    AddOne(type); 
} 

public void AddOne(MyRefType type) { 
    type.Value++; 
} 

這裏會發生什麼事,是類type現在將有一個值。這是參考類型的性質。如果typestruct,則它在Foo方法內的值仍然爲0,因爲複製是由持有對象的引用而不是持有的。

現在我希望你能理解按值傳遞引用類型的同義詞,讓我們來談談如何通過引用傳遞引用類型。這是通過使用outref關鍵字完成的。請立刻注意,在CLR中,out在技術上不存在,只有refout實際上在元數據中表示爲ref,其中應用了特殊屬性,並且它們應該被視爲相同。

現在我們假設我們想在添加之前在AddOne方法中重新指定我們的參考類型。

public class MyRefType 
{ 
    public int Value { get; set; } 
} 

public void Foo() { 
    MyRefType type = new MyRefType(); 
    AddOne(type); 
} 

public void AddOne(MyRefType type) { 
    type = new MyReferenceType(); 
    type.Value++; 
} 

因爲我們仍然按值傳遞我們的參考類型,type在法的價值Foo仍然是0。所有我們所做的是在內存初始化另一個存儲位置,而不是重新分配原始存儲位置) 。但是,我們要實際參考通過我們的引用類型,所以我們應該真正做到:

public class MyRefType 
{ 
    public int Value { get; set; } 
} 

public void Foo() { 
    MyRefType type = new MyRefType(); 
    AddOne(ref type); 
} 

public void AddOne(ref MyRefType type) { 
    type = new MyReferenceType(); 
    type.Value++; 
} 

現在的typeFoo方法的值爲1。這是因爲我們重複使用相同的存儲位置指向的參考,而不是在內存中創建一個新的位置。

那麼這一切如何適用於out?請記住,out與使用不同用法語義的ref相同。與ref不同的是,您可以不使用引用進行初始化,而使用out時,必須在方法返回之前明確初始化它。

因此,在您使用out的情況下,完全沒有必要,因爲您已經擁有現成的參考語義。我希望這個清理一下。

+0

「這會導致副作用你的API的調用者可能不會期望「,除了在C#中,調用者不需要」out「。所以我不同意。 – payo 2012-04-25 03:29:39

+0

我有一個錯字(現在> 5分鐘過去了,我無法修復它:) - 「C#'out'是必需的」。你反駁說,並不是每個人都明白'out'是什麼,我仍然不同意......如果不是我們正在討論這個HAHA的問題:)我同意總體上避免'out',但'不惜一切代價'對於使用它來說太重了。否則我喜歡你的帖子。 – payo 2012-04-25 03:46:06

2

您在代碼示例中誤用了關鍵字out。您的List<_File>作爲參考類型傳遞,並且對該列表所做的任何更改都將影響呼叫者的列表。當您要重新分配呼叫者的標識符時,您只需傳遞out參數。

考慮:

public bool Foo() 
{ 
    List<int> list = new List<int>(); 
    list.Add(1); 
    Bar(out list); 
    return list.Contains(1); 
} 

public void Bar(out List<int> list) 
{ 
    list = new List<int>(); 
    list.Add(2); 
} 

Foo將返回false。

如果,另一方面,Bar是這樣的:

public void Bar(List<int> list) 
{ 
    list = new List<int>(); 
    list.Add(2); 
} 

然後Foo將返回true。有關更多詳細信息,請參閱out

如果不使用out,您的代碼將照原樣工作。而且,實際上不會編譯 - 並且編譯它會增加您嘗試執行的操作的不必要的複雜性。請勿在您的情況下使用out