[英]How to use delegates in correct way / Understanding delegates
使用 - C#(。Net Framework 4.5,Visual Studio 2012)
我試着理解像代表這樣的主題,目前我有幾點,必須為我澄清。 我在互聯網上發現了很多不同的信息來描述如何使用它,但是對我來說理解這個主題有點復雜。
據我所知,我必須做一些使用委托的事情:
所有描述如下所示
問題 - 我是否正確理解了所有或者我錯了 - 請澄清一下。
另外一個關於DELEGATE的問題 - 哪里更好地將代碼放在DELEGATE中 - 在Console C#應用程序中我可以在任何使用過的Namespace的地方創建它 - 我可以在下面看到。
但也許有一些建議/要求不僅為控制台應用程序而且為WinForms,WPF等放置委托。
這個主題對我來說是新的,我花了一天時間來理解它,但仍然有點(或更多)與此混淆,最后創建這篇文章以獲得更好和清晰的理解。 認為這是非常強大的東西。
編輯
namespace SimpleCSharpApp
{
delegate void myDelagate ();
}
Ho-ho ..你有些搞砸了。 直到我看到VS的屏幕截圖,並在“委托”聲明下帶有紅色下划線,我還沒有完全掌握你想要說明的問題。
首先, public void delegate zczcxxzc
忘掉public void delegate zczcxxzc
行。 這有點特別。 首先,讓我們看一些代表的標准種類*)。
最基本的兩個是:
System.Action
System.Func
兩者都是通用的,並且第一次看到它們的簽名,它們看起來可能過於復雜。 但是,它們真的非常簡單。
首先,讓我們限制裸,無參數, System.Action
。
private static void myFunction1() { Console.WriteLine("Hello!"); }
private static void myFunction2() { Console.WriteLine("Bye!"); }
private static void myFunction0() { return; }
... // in some function, i.e. Main()
Action myDelegate = null;
myDelegate = new Action( myFunction1 );
myDelegate(); // writes "Hello!"
myDelegate = new Action( myFunction2 );
myDelegate(); // writes "Bye!"
myDelegate = new Action( myFunction3 );
myDelegate(); // does "nothing"
就像“int”包含一個數字,“string” - 文本,“委托”包含有關“可調用的東西”的信息,或者,使用某些術語,“可調用的東西”。
首先,我創建了一個記住“myFunction1”的“Action”類型的委托 。 然后我調用/調用該委托 - 它導致被記住的函數被調用。
然后,我創建一個類型為“Action” 的委托 ,記住“myFunction2”。 然后我調用/調用該委托 - 它導致被記住的函數被調用。
最后,我創建一個類型為“Action” 的委托 ,它記住了“myFunction3”。 然后我調用/調用該委托 - 它導致被記住的函數被調用,沒有任何反應 - 但只是因為目標函數什么也沒做。
請注意,我故意說“創建了一個代表”。 每次執行new Action
,都會創建一個新委托。 “委托”只是一個對象,如String“foo”或float [] {1.2,4.5}。
另請注意,此處使用的創建委托的完整語法是new Action(...)
。 就像創建任何對象一樣 - 新的+ typename +構造參數。 另一個標志,“代表”只是一個對象。
另外需要注意的是我沒有編寫new Action( myFunction1() )
。 我不想調用該方法並獲取其結果並將該結果提供給Action的構造函數。 我寫了new Action( myFunction1 )
。 我把函數本身給了構造函數。
那么,什么是“行動”呢? System.Action是一個類。 像String,或Socket或WebClient。 這里沒什么特別的。 所以,我們有一個“類”,它的對象可以記住應該被調用的函數。 涼。
因此,有些人將代表與“函數指針”進行比較。 但那並不完全正確。 函數指針可以記住要調用的函數 。 代表們可以記住要調用的方法 。 記得區別嗎? 在上面的例子中,我故意在每個myFunction
寫static
。 這些可以被稱為無對象/無目標。 你需要他們的名字,你可以從任何地方打電話給他們。 要打電話給他們,一個簡單的啞指針就足夠了。
現在,代表們可以做得更多。 他們可以研究方法 。 但是需要針對對象調用方法。
class GuineaPig
{
public static void Squeak() { Console.WriteLine("Ieek!"); }
public void Well() { Console.WriteLine("actually"); }
public void IDontKnow() { Console.WriteLine("what they do"); }
}
GuineaPig.Squeak(); // says 'ieek'
Action myDelegate = null;
myDelegate = new Action( GuineaPig.Squeak );
myDelegate(); // writes "ieek"
// GuineaPig.Well(); // cannot do!
// myDelegate = new Action( GuineaPig.Well ); // cannot do!
好吧,在其他類中創建一個靜態函數的委托很容易 - 只需要確切地說什么是函數來自什么類。 再次就像打電話一樣,但沒有括號。
但是,如果您嘗試取消注釋對非靜態方法的引用,它將無法編譯。 看看GuineaPig.Well
- 這很明顯。 它不是靜態的,需要針對OBJECT而不是CLASS進行調用。 出於同樣的原因,無法創建委托。 我們來解決這個問題:
class GuineaPig
{
public void Well() { Console.WriteLine("actually"); }
public void IDontKnow() { Console.WriteLine("what they do"); }
}
GuineaPig myPiggie = new GuineaPig();
myPiggie.Well(); // ok! writes "actually"
Action myDelegate = null;
myDelegate = new Action( myPiggie.Well ); // ok!
myDelegate(); // ok! writes "actually".
請注意在創建委托期間如何將classname替換為objectvariable。 語法被保留:就像調用一樣,但沒有parens。 但是,關於“方法”與“功能”的所有大驚小怪......
代理人不僅可以存儲要調用的“方法”,還可以存儲調用它們的對象 。
class GuineaPig
{
public string Name;
public void Well() { Console.WriteLine("I'm " + Name); }
}
GuineaPig myPiggie1 = new GuineaPig { Name = "Bubba" };
GuineaPig myPiggie2 = new GuineaPig { Name = "Lassie" };
Action myDelegate = null;
myDelegate = new Action( myPiggie1.Well );
myDelegate(); // -> Bubba
myDelegate = new Action( myPiggie2.Well );
myDelegate(); // -> Lassie
myPiggie1 = myPiggie2 = null;
myDelegate(); // -> Lassie
現在,這是普通函數指針無法做到的。 (盡管你可以使用非常智能的函數指針......但是,讓我們離開它)。
注意如何在“pig#2”上調用“Well”的事實存儲在委托對象中。 “myPiggie2”變量無關緊要。 我可以使它無效。 代表記住了目標和方法。
System.Action只是其中之一。 它是最簡單的,沒有參數,沒有返回..但是它們中有很多,它們可以獲取參數( Action<string, int>
)它們可以返回值( Func<int>
)或兩者( Func<string,int>
) 。 然而,不斷說Func<int,float,string,int,int,bool,decimal>
有些......模糊不清。
好。 讓我們終於明白了這一切的嘮叨。 對不起,如果你知道所有這些,但我想要清楚。
“代表”就是要記住“目標”和“方法”。 實際上,如果你在調試器中檢查一個委托(或檢查Intellisense在“點”之后說的內容),你會看到兩個屬性,Target和Method。 他們正是他們的名字所代表的。
讓我們假設您想要創建自己的委托類型。 一種不會被稱為Func<int,int,int,bool,bool,Zonk,string>
,而是“MyStudentFilteringDelegate”。
現在,重點是,在C#中你不能輕易地獲取函數的&
(地址) ,也不能重載operator() 。 這導致您無法編寫自己的委托類。
你不能只寫:
class MyStudentFilteringDelegate
{
public object Target;
public somethingstrange* MethodPointer;
// other code
}
因為,即使你真的設法遵循這個想法,最后在某個地方你會發現:
MyDelegate dd = new MyDelegate ( ... );
dd(); // is just impossible!!!
至少,在當前的C#版本4.5或5中。
你不能重載“call”/“invoke”操作符,因此你將無法完全實現自己的,自定義命名的委托類型。 你永遠都會被動作和Funcs困住。
現在回想起public void delegate xxx
下的紅色下划線我請你暫時忘記。
public bool delegate MyStudentFilteringDelegate( Student stud );
此行不會創建任何委托 。 該行定義了委托的類型 。 它與Func<Student,bool>
完全相同,帶有自定義名稱。 *)
實際上,編譯器將行轉換為:
public class MyStudentFilteringDelegate : some_framework_Delegate_class
{
// object Target {get;} - inherited from base class
// MethodInfo Method {get;} - inherited from base class, too
}
所以它是一個類 ,所以你現在可以創建一個委托對象:
var dd = new MyStudentFilteringDelegate ( ... ); // like normal class!
dd(); // ok!;
由於類是特殊的,編譯器生成的,它可以破壞規則。 它的'call'/'invoke'運算符被重載,所以你可以“調用”委托,因為它是一個方法。
請注意,盡管有一些奇怪的表示法:
public bool delegate MyStudentFilteringDelegate( Student stud );
MyStudentFilteringDelegate
是一個類 ,就像Action或Func或String或WebClient一樣。 delegate
關鍵字只是編譯器的標記,以便知道它應該應用於該行的轉換以生成適當的“委托類型”(類)。
現在,要真正回答你的另一個問題:
在您放置委托類型聲明的地方真的無關緊要。 你可以在任何你喜歡的地方寫public void delegate XYZ(...)
。 就像你可以在任何地方放置一個類聲明一樣。
您可以將類聲明放在默認(無命名空間)范圍,某個命名空間或類中。 因此,由於委托類型只是一個類,您還可以在默認(無命名空間)范圍,某個命名空間或類內部處理新的委托類型:
public class Xc {}
public void delegate Xd();
namespace WTF {
public class Xc {}
public void delegate Xd();
class Whatever {
public class Xc {}
public void delegate Xd();
}
}
請注意,我完全故意將它們命名為相同。 那不是錯誤。 首先命名為::global.Xc
和::global.Xd
,第二對命名為WTF.Xc
和WTF.Xd
,最后一對命名為WTF.Whatever.Xc
和WTF.Whatever.Xd
。 就像普通的clases一樣。
要確定放置這些聲明的位置,請使用與您用於類的規則相同的規則。 IE瀏覽器。 如果您將文本處理類放在命名空間MyApp.Text.Parsing
,那么與該文本處理相關的所有委托類型也應該位於該命名空間中。 但是,即便如此,這純粹是化妝和組織的。 在適合您的任何范圍內放置/定義它們。
編輯:*)實際上,從歷史上看,它是完全相反的。 delegate
關鍵字和編譯器技巧比Action和Func類舊 。 在.Net 2.0中,Action / Func不存在。 創建/使用委托的唯一方法是定義自己的新委托類型(或在系統的名稱空間深處找到/猜測一些合適的委托類型)。 請記住,每個新的委托類型都是一個新類。 不能轉換為任何其他類,甚至不能同樣看起來。 令人沮喪的是令人沮喪且難以保持,在.Net 3.5中,他們最終在框架中包含了“通用通用委托類型”。 從那時起,Action / Func越來越常被使用,因為即使它們更難閱讀,它們也是通用的。 System.Func<Student,bool>
可以“隨處”傳遞,並且你沒有問題, from one library does not match
'bool委托StudentFilter() from one library does not match
的bool委托StudentSelector()` from one library does not match
。
c#中的委托有點像C ++中函數指針的替代品。 它們有很多用途。 您可以使用它們:
根據我的經驗,第一次使用是最常見的。
Button.Click += delegate(object sender, EventArgs args) => {/*you do something here*/};
lamba表達式可以簡化:
Button.Click += (sender, args) => {/*you do something here*/};
您為按鈕單擊提供了一些行為,而無需為其創建單獨的方法。
關於問題的第二部分,我通常將代理聲明放在單獨的文件中。
委托聲明可以放在具有公共和內部可訪問性的任何源文件中。 我個人將它們放在最適用的類源的頂部。
例如,如果我有一個專門的事件委托,它將CustomEventArgs作為參數,我將把委托聲明放在該文件的頂部:
namespace MyNamespace {
public delegate void SpecialEventDelegate(object sender, CustomEventArgs e);
public class CustomEventArgs : EventArgs {
// implementation details
}
}
另一個選擇是將代理人放在一個單獨的源文件中......我沒有看到這樣做,並且會受到編碼指南的約束。
代表一般有兩個資格:
第一個資格是代表的聲明:
public delegate void SpecialEventDelegate(object sender, CustomEventArgs e);
...或使用Func<TResult>
或Action
:
Action<object, CustomEventArgs> // similar declaration to the SpecialEventDelegate above
由於事件處理程序(聲明的委托)通常沒有返回類型( void
),因此不會使用Func<TResult>
。 Func委托需要返回類型:
Func<bool> // a delegate that return true/false
有關Func<TResult>
和Action
更多信息,請參閱鏈接的MSDN文章。 為了完整性和對新的聲明方法的見解,我只提供了對這些的引用。 Func<TResult>
和Action
是delegate
專用包裝器。
委托的第二個資格是對方法或類成員的引用。 我可能有一個私有方法,作為特定需求的處理程序。 假設FileIO對象需要針對不同類型文件的特定文件處理程序 - 即.XML,.TXT,.CSV:
namespace MyNamespace {
public delegate Stream OpenFile(FileInfo FileSpec);
}
現在,任何對象都可以根據文件類型實現自己的OpenFile
定義,但必須返回Stream
對象。
class XMLHandler : IFileHandler {
private OpenFile xmlFileReader;
// implementation of interface
public OpenFile FileHandler {
get { return xmlFileReader; }
}
public XMLHandler(){
xmlFileReader = MyXmlFileReader; // references the private method in this class
}
private Stream MyXmlFileReader(FileInfo XmlFileSpec) {
// implementation specific to this class
}
}
interface IFileHandler {
OpenFile FileHandler { get; }
}
通過使用委托聲明和接口,我們可以將XMLHandler
對象作為IFileHandler
傳遞,並且只通過FileHandler
屬性公開委托,而不暴露整個對象。 請注意,委托引用的方法是私有的。 這是委托(和Func,Action)的特殊好處。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.