I recently came upon a static method declared as:
public class Foo
{
public static Func<HtmlHelper, PropertyViewModel, string> Render = (a, b) =>
{
a.RenderPartial(b);
return "";
};
}
Intellisense suggests the usage is (for example):
string s = Foo.Render(htmlHelper, propertyViewModel);
It would seem then that the following is equivalent:
public static string Render(HtmlHelper a, PropertyViewModel b)
{
a.RenderPartial(b);
return "";
}
A) What is the name of the first style? I realize it's using lambdas; it's the =
sign that is tripping me up. I can't tokenize it ;)
B) If the two code blocks are equivalent, what is the benefit of using the former over the latter?
Ok, for clarity I'm going to write the two out again (and slightly modify the method to make it shorter)
public static Func<HtmlHelper, PropertyViewModel, string> RenderDelegate = (a, b) =>
{
return a.RenderPartial(b);
};
public static string RenderMethod(HtmlHelper a, PropertyViewModel b)
{
return a.RenderPartial(b);
}
Firstly note that RenderDelegate
is (as S. DePouw writes), just a fancy way of using lambda syntax to write the following:
public static Func<HtmlHelper, PropertyViewModel, string> RenderDelegate =
delegate(HtmlHelper a, PropertyViewModel b)
{
return a.RenderPartial(b);
};
The difference between RenderMethod
and RenderDelegate
is that RenderMethod
is a method, wheras RenderDelegate
is a delegate, or more specifically a field of type Delegate. This means that RenderDelegate can be assigned to.
A delegate is a type. From the MSDN documentation:
A delegate is a type that defines a method signature, and can be associated with any method with a compatible signature.
Essentially you can think of a delegate as a reference / pointer to a method, however the method that the delegate points to has to match the signature that the delegate is expecting. So for example Func<HtmlHelper, PropertyViewModel, string>
is a Delegate that expects methods with the signature string MyMethod(HtmlHelper, PropertyViewModel)
and so we are able to assign methods with that signature to that delegate like this:
RenderDelegate = RenderMethod;
Its important to note the difference between the Delegate type (note the capital D) and the delegate keyword (lower case d). In your example your using the Func<>
generic object to condense your code, however its kind of obscuring whats really going on here. Func<HtmlHelper, PropertyViewModel, string>
is a type which inherits from Delegate
, and you could use the delegate keyword to delcare an equivalent type:
delegate string MyFunction<HtmlHelper helper, PropertyViewModel string>;
static MyFunction RenderDelegate = RenderMethod;
When we assigned RenderDelegate in the first example, we didnt set RenderDelegate to an existing named method, instead we declared a new method in-line. This is known as an Anonymous Method and works because we are able to pass a code block (also declared using the delegate keyword) as a delegate parameter:
Back to the original syntax - your example is using lambda syntax to delcare an anonymous delegate in a funny way. Lambda expressions are good way of declaring short inline methods which might commonly be used when dealing with lists, for example supposing we want to sort a list of HtmlHelper objects by their Name. The way of doing this is to pass a Delegate that compares two HtmlHelper objects to the lists Sort method, the sort method then uses that delegate to compare and sort the elements in the list:
static int MyComparison(HtmlHelper x, HtmlHelper y)
{
return x.Name.CompareTo(y.Name);
}
static void Main()
{
List<HtmlHelper> myList = GetList();
myList.Sort(MyComparison);
}
To avoid having loads of short methods scattered around, you can use anonymous methods to delcare the sorting method in-line. Whats also really useful about this is that the in-line method has access to variables declared in the containing scope:
int myInt = 12;
List<HtmlHelper> myList = GetList();
myList.Sort(
delegate (HtmlHelper x, HtmlHelper y)
{
return x.Name.CompareTo(y.Name) - myInt;
});
Thats still fairly quite a lot of typing however, and so the lambda sytax was born and now you can do this instead:
List<HtmlHelper> myList = GetList();
myList.Sort((x, y) => {return x.Name.CompareTo(y.Name)});
Declaring "normal" methods in this way however seems to be completely pointless to me (and makes my eyes bleed)
Delegates are incredibly useful and are (among other things) the cornerstone of the .Net event system. Some more reading to clear things up a bit:
A) The style is that of using delegates. The following is equivalent:
public static Func<HtmlHelper, PropertyViewModel, string> Render =
delegate(HtmlHelper a, PropertyViewModel b)
{
a.RenderPartial(b);
return "";
};
B) The benefit is that you could then treat Render as a variable within another method. In this particular instance, however, they're more or less the same benefits-wise (although the latter is a bit easier to comprehend).
For the most part they seem functionally equivalent. In fact you can pass around a normal method as a variable.
But there are subtle differences like being able to redefine the function to be something else. It is probably also different if you're using reflection, e.g. it probably isn't returned in the list of methods on the class. (Not 100% sure on the reflection part)
The following shows passing a method as a variable as well as how the 2nd way allows redefining the Func that wouldn't be possible if it were a normal method.
class Program
{
static void Main(string[] args)
{
Console.WriteLine(GetFunc()); //Prints the ToString of a Func<int, string>
Console.WriteLine(Test(5)); //Prints "test"
Console.WriteLine(Test2(5)); //Prints "test"
Test2 = i => "something " + i;
Console.WriteLine(Test2(5)); //Prints "something 5"
//Test = i => "something " + i; //Would cause a compile error
}
public static string Test(int a)
{
return "test";
}
public static Func<int, string> Test2 = i =>
{
return "test";
};
public static Func<int, string> GetFunc()
{
return Test;
}
}
This just got me thinking... if all methods were declared this way, you could have real first class functions in C#... Interesting....
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With