views:

2891

answers:

4

Hi,

Sorry if this is basic but I was trying to pick up on .Net 3.5.

Question: Is there anything great about Func<> and it's 5 overloads? From the looks of it, I can still create a similar delgate on my own say, MyFunc<> with the exact 5 overloads and even more.

eg: public delegate TResult MyFunc<TResult>() and a combo of various overloads...

The thought came up as I was trying to understand Func<> delegates and hit upon the following scenario:

Func<int,int> myDelegate = (y) => IsComposite(10);

This implies a delegate with one parameter of type int and a return type of type int. There are five variations (if you look at the overloads through intellisense). So I am guessing that we can have a delegate with no return type?

So am I justified in saying that Func<> is nothing great and just an example in the .Net framework that we can use and if needed, create custom "func<>" delegates to suit our own needs?

Thanks,

+7  A: 

The Func family of delegates (and their return-type-less cousins, Action) are not any greater than anything else you'd find in the .NET framework. They're just there for re-use so you don't have to redefine them. They have type parameters to keep things generic. E.g., a Func<T0,bool> is the same as a System.Predicate<T> delegate. They were originally designed for LINQ.

You should be able to just use the built-in Func delegate for any value-returning method that accepts up to 4 arguments instead of defining your own delegate for such a purpose unless you want the name to reflect your intention, which is cool.

Cases where you would absolutely need to define your delegate types include methods that accept more than 4 arguments, methods with out, ref, or params parameters, or recursive method signatures (e.g., delegate Foo Foo(Foo f)).

Mark Cidade
+2  A: 

In addition to Marxidad's correct answer:

  • It's worth being aware of Func's related family, the Action delegates. Again, these are types overloaded by the number of type parameters, but declared to return void.
  • If you want to use Func/Action in a .NET 2.0 project but with a simple route to upgrading later on, you can cut and paste the declarations from my version comparison page. If you declare them in the System namespace then you'll be able to upgrade just by removing the declarations later - but then you won't be able to (easily) build the same code in .NET 3.5 without removing the declarations.
Jon Skeet
+14  A: 

The greatness lies in establishing shared language for better communication.

Instead of defining your own delegate types for the same thing (delegate explosion), use the ones provided by the framework. Anyone reading your code instantly grasps what you are trying to accomplish.. minimizes the time to 'what is this piece of code actually doing?' So as soon as I see a

  • Action delegate would trigger 'some method that just does something.. no input or output reqd'
  • Comparison = 'some method that compares two objects of the same type and returns an int to indicate order'
  • Converter = 'transforms Obj A into equivalent Obj B'
  • Eventhandler = 'response/handler to an event raised by some object given reqd input in the form of an event argument'
  • Func delegate, I'd associate that to 'some method that takes n parameters, computes something and returns a result'.
  • Predicate = 'evaluate input object against some criteria and return pass/fail status as bool'

I don't have to dig deeper than that unless it is my immediate area of concern. So if you feel the delegate you need fits one of these needs, use them before rolling your own.

Disclaimer: Personally I like this move by the language designers.

Counter-argument : However, Sometimes defining your delegate may help communicate intent better. e.g. System.Threading.ThreadStart over System.Action. or as Jon mentions Predicate over Func<T, TResult>. So its a judgment call in the end.

Gishu
man.. just got skeeted :)
Gishu
Nah, your answer's better. I'm glad that Func/Action are there, and they're *usually* sufficient - but I think there are still times when it's worth defining your own delegates. A good example is predicate - it's not strictly necessary when there's alreay Func<T,bool> but it indicates intent.
Jon Skeet
Likewise you may well want to provide a named delegate that *could* be represented as Func<string, string, int> (or whatever) - calling it MatchCounter makes the intention clearer. It's a balancing act. The advantage of using Func of course is that you then don't need to look up the delegate...
Jon Skeet
Well I intentionally left that part out from my answer but seems like it needs to be included to complete it.
Gishu
+1  A: 

Decoupling dependencies and unholy tie-ups is one singular thing that makes it great. Everything else one can debate and claim to be doable in some home-grown way.

I've been refactoring slightly more complex system with an old and heavy lib and got blocked on not being able to break compile time dependency - because of the named delegate lurking on "the other side". All assembly loading and reflection didn't help - compiler would refuse to just cast a delegate() {...} to object and whatever you do to pacify it would fail on the other side.

Delegate type comparison which is structural at compile time turns nominal after that (loading, invoking). That may seem OK while you are thinking in terms of "my darling lib is going to be used forever and by everyone" but it doesn't scale to even slightly more complex systems. Fun<> templates bring a degree of structural equivalence back into the world of nominal typing . That's the aspect you can't achieve by rolling out your own.

Example - converting:

class Session ( 
    public delegate string CleanBody();    // tying you up and you don't see it :-)
    public static void Execute(string name, string q, CleanBody body) ... 

to:

    public static void Execute(string name, string q, Func<string> body)

Allows completely independent code to do reflection invocation like:

Type type = Type.GetType("Bla.Session, FooSessionDll", true); 
MethodInfo methodInfo = type.GetMethod("Execute"); 

Func<string> d = delegate() { .....}  // see Ma - no tie-ups :-)
Object [] params = { "foo", "bar", d};
methodInfo.Invoke("Trial Execution :-)", params);

Existing code doesn't notice the difference, new code doesn't get dependence - peace on Earth :-)

ZXX