简体   繁体   中英

How to use delegates in correct way / Understanding delegates

use - C# (.Net Framework 4.5, Visual Studio 2012)

I try to understand such theme like Delegate, and currently I have few points, that must be clarified for me. I found a lot of different information in internet that describe how to use it, but it's a little bit complicated to understanding for me this theme.

As I understand I must to do few thing for using delegate:

  • Create some entity for work with it (that require creating some delegate)
  • Declare a delegate type
  • Create some method where I call to delegate
  • In main class call delegate with required method that use entity (from first point)

all described I show below

理解分流

The question - am I correctly understand all or maybe I'm wrong - please clarify it.

Also another question regarding DELEGATE - where is better to put code with DELEGATE - in Console C# application I can create it in any place of used Namespace - as I can show below.

放置dalagete

But maybe there is some recommendation/requirements for placing delegate not only for Console application but also for WinForms, WPF etc.

This theme is new for me and I spend a day for understanding it, but still a little bit (or a more) confused with this, finally create this post for better and clear understanding. think it's very powerfull stuff.

EDIT

namespace SimpleCSharpApp
{
   delegate void myDelagate ();
}

Ho-ho.. you've got something messed up. I didn't quite grasp what problem you are trying to state until I saw the screenshot from VS with red underline under "delegate" declaration.

First, forget about the public void delegate zczcxxzc line for a moment. It is somewhat special. First, let's see some standard kinds *) of delegates.

The two most basic ones are:

  • System.Action
  • System.Func

Both are generic and looking at their signature for the first time, they may seem overly complex. But, they are really very simple.

For starters, let's limit to bare, parameterless, 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"

Just like "int" holds a number, "string" - text, a "delegate" holds information about "something callable", or, to use some terminology, "something invokable".

First, I create a delegate of type "Action" that remembers "myFunction1". Then I invoke/call that delegate - it results in the remembered function being called.

Then, I create a delegate of type "Action" that remembers "myFunction2". Then I invoke/call that delegate - it results in the remembered function being called.

Finally, I create a delegate of type "Action" that remembers "myFunction3". Then I invoke/call that delegate - it results in the remembered function being called, and nothing happens - but only because the target function did nothing.

Please note that I deliberately say "created a delegate". Each time a new Action is executed, a new delegate is created. A "delegate" is just an object, like String "foo" or float[] {1.2, 4.5}.

Also, note that the full syntax for creating delegates used here is new Action(...) . Just like creating any object - new + typename + construction parameters. Yet another sign that a "delegate" is just an object.

Another thing to note is that I did not write new Action( myFunction1() ) . I did not want to CALL the method and get its result and give that result to the constructor of Action. I wrote new Action( myFunction1 ) . I gave the function itself to the constructor.

But, then, what is an "Action"? System.Action is a class. Like String, or Socket or WebClient. Nothing special here. So, we have a "class" whose objects can remember what-function-should-be-called. Cool.

Therefore some compare delegates to "function pointers". But that's not fully right. Function pointers can remember what function to call. Delegates can remember what method to call. Remember the difference? In my example above, I deliberately wrote static at each myFunction . Those can be called object-less/target-less. You just need their name and you can call them from anywhere. To call them, a simple dumb pointer would be enough.

Now, delegates can do more. They can work on methods . But methods need to be invoked against an object..

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!

Ok, making a delegate to a static function in other class was easy - just had to say exactly what-function-from-what-class. Again just like calling, but without parenthesis.

But, if you try uncommenting the references to non-static methods, it will not compile. Looking at the GuineaPig.Well - that's obvious. It's not static, it needs to be called against OBJECT, not CLASS. For that very same reason, the delegate could not be created. Let's fix that:

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".

Note how classname was replaced with objectvariable during the creation of the delegate. The syntax is preserved: just like calling, but without parens. But, what's all the fuss about "methods" vs "functions"..

Delegates can store not only 'what method' to be called, but also what object to call them upon.

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

Now that is something you cannot do with plain function pointers. (Although with very smart function pointers you could.. but, let's leave that).

Note how the fact that "Well" was to be called on "pig#2" was stored inside the delegate object. The "myPiggie2" variable was irrelevant. I could nullify it. The delegate remembered both target and method.

System.Action is just one of the family. It's the simpliest, no params, no returns.. But there are many of them, they can get parameters ( Action<string, int> ) they can return values ( Func<int> ) or both ( Func<string,int> ). Yet, constantly speaking in terms of Func<int,float,string,int,int,bool,decimal> is somewhat ... obscure.

Ok. Let's finally get to the point of all this babbling. Sorry if you knew all of that, but I wanted to be clear.

A "delegate" is all about remembering "target" and "method". In fact, if you ever inspect a delegate in the debugger (or check what Intellisense says after the "dot"), you will see two properties, Target and Method. They are just what their names stand for.

Let's imagine you want to create your own type of a delegate. A type that would not be called Func<int,int,int,bool,bool,Zonk,string> , but rather, "MyStudentFilteringDelegate".

Now, whole point is, that in C# you cannot take & (address) of a function easily, and also you cannot overload the operator() . This results in you being unable to write your own delegate-like classes.

You cannot just write:

class MyStudentFilteringDelegate
{
     public object Target;
     public somethingstrange*  MethodPointer;

     // other code
}

because, even if you actually managed to follow that idea, somewhere at the end you would find that:

MyDelegate dd = new MyDelegate ( ... );
dd(); // is just impossible!!!

At least, in current C# version 4.5 or 5.

You just cannot overload the "call"/"invoke" operator, hence you would not be able to fully implement your own, custom-named, delegate type. You'd be stuck at Actions and Funcs for ever.

Now recall that red underline under the public void delegate xxx I asked you to temporarily forget.

public bool delegate MyStudentFilteringDelegate( Student stud );

This line does not create any delegate . This line defines a type of a delegate . It is perfectly the same as Func<Student,bool> , with custom name. *)

In fact, the compiler converts the line to:

public class MyStudentFilteringDelegate : some_framework_Delegate_class
{
    // object Target {get;} - inherited from base class
    // MethodInfo Method {get;} - inherited from base class, too
}

so that it is a class , and so that you can now create a delegate object:

var dd = new MyStudentFilteringDelegate ( ... ); // like normal class!
dd(); // ok!;

As the class is special, compiler-generated, it can break rules. It's 'call'/'invoke' operator is overloaded, so you can "call" the delegate as it were a method.

Please note that despite the strange notation:

public bool delegate MyStudentFilteringDelegate( Student stud );

the MyStudentFilteringDelegate is a class , just like Action or Func or String or WebClient are. The delegate keyword is just a marker for the compiler to know what transformation it should apply to that line to generate a proper "delegate type" (class).

Now, to actually answer your other question:

It is really irrelevant where you put the delegate type declaration . You may write public void delegate XYZ(...) anywhere you like. Just as you may place a class declaration just about anywhere.

You may place class declarations at default(no-namespace) scope, at some namespace, or inside a class. So, as the delegate-type is just a class, you may also delcare new delegate types at default(no-namespace) scope, at some namespace, or inside a class:

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();
    }
}

Note that I completely deliberately named them identically. That's no error. First are named ::global.Xc and ::global.Xd , the second pair is named WTF.Xc and WTF.Xd and the final pair is named WTF.Whatever.Xc and WTF.Whatever.Xd . Just like normal clases.

To decide where to place those declarations, use the same rules as you use for classes. Ie. if you place text-processing classes in namespace MyApp.Text.Parsing , then all the delegatetypes related to that text-processing should sit in that namespace too. But, even so, that's purely cosmetical and organizational. Place/define them at whatever scope it makes sense for you.

EDIT: *) actually, historically, it was all other way around. The delegate keyword and compiler trick is older than the Action and Func classes. At .Net 2.0, the Action/Func did not exist. The only way to create/use a delegate was to define your own new delegate type (or find/guess some suitable one somewhere deep in the system's namespaces). Keep in mind that every new delegate-type is a new class. Not convertible to any other class, not even similarly-looking. It was so frustratingly tiresome and hard to mantain, that at .Net 3.5 they finally included "general purpose generic delegate types" in the framework. From that point of time, Action/Func are increasingly often used, because even if they are harder to read, they are .. universal. System.Func<Student,bool> can be passed "anywhere", and you don't have a problem that 'bool delegate StudentFilter() from one library does not match bool delegate StudentSelector()` from anoother one.

Delegates in c# are a little bit like a replacement for function pointers in C++. There are plenty of usages for them. You can use them to:

  1. Crate an inline implementation for an event.
  2. Pass a callback to a function.
  3. Create a array of "functions" you can call in a loop.

From my experience the first usage is the most common.

Button.Click += delegate(object sender, EventArgs args)  => {/*you do something here*/};

Which can be simplified by a lamba expression:

Button.Click += (sender, args) => {/*you do something here*/};

You provide some behavior to a button click not having to create a separate method for it.

Regarding to the second part of your question, I usually put delegates declarations in separate files.

Delegate declarations can be placed in any source file with public and internal accessibility. I personally place them at the top of a class source for which it is most applicable.

For instance if I have a specialized event delegate that takes CustomEventArgs as a parameter, I will put the delegate declaration at the top of that file:

namespace MyNamespace {
   public delegate void SpecialEventDelegate(object sender, CustomEventArgs e);

   public class CustomEventArgs : EventArgs {
      // implementation details
   }
}

Another option is to put delegates together in a single source file ... I haven't seen this done and would be subject to coding guidelines.

A delegate generally has two qualifications:

  • A declaration of a method or class member
  • A reference to a method or class member

The first qualification is the declaration of a delegate:

   public delegate void SpecialEventDelegate(object sender, CustomEventArgs e);

... or using Func<TResult> or Action :

   Action<object, CustomEventArgs> // similar declaration to the SpecialEventDelegate above

Since an event handler (declared delegate) generally does not have a return type ( void ), a Func<TResult> would not be used. A Func delegate requires a return type:

   Func<bool> // a delegate that return true/false

See the linked MSDN articles for more on Func<TResult> and Action . I only included references to these for completeness and insight on newer declaration methods. Func<TResult> and Action are specialized wrappers for the delegate .

The second qualification for a delegate is a reference to a method or class member. I may have a private method that acts as a handler for specific needs. Say a FileIO object needs a specific file handler for different types of files - that is, .XML, .TXT, .CSV:

namespace MyNamespace {

   public delegate Stream OpenFile(FileInfo FileSpec);

}

Now any object can implement it's own OpenFile definition based on the file type but must return a Stream object.

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; }

}

By using both a delegate declaration and an interface, we can pass the XMLHandler object as an IFileHandler and only expose the delegate via the FileHandler property without exposing the entire object. Note that the method referenced by the delegate is private. This is a special benefit of the delegate (and Func, Action).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM