Reflecting parameter name: abuse of C# lambda expressions or Syntax brilliance?
I am looking at the MvcContrib Grid component and I'm fascinated, yet at the same time repulsed, by a syntactic trick used in the Grid syntax:
.Attributes(style => "width:100%")
The syntax above sets the style attribute of the generated HTML to width:100%
. Now if you pay attention, 'style' is nowhere specified, is deduced from the name of the parameter in the expression! I had to dig into this and found where the 'magic' happens:
Hash(params Func<object, TValue>[] hash)
{
foreach (var func in hash)
{
Add(func.Method.GetParameters()[0].Name, func(null));
}
}
So indeed, the code is using the formal, compile time, name of parameters to create the dictionary of attribute name-value pairs. The resulted syntax construct is very expressive indeed, but at the same time very dangerous. The general use of lambda expressions allows for replacement of the names used without side effect. I see an example in a book that says collection.ForEach(book => Fire.Burn(book))
I know I can write in my code collection.ForEach(log => Fire.Burn(log))
and it means the same thing. But with the MvcContrib Grid syntax here all of the sudden I find code that actively looks and makes decissions based on the names I choose for my variables!
So is this common practice with the C# 3.5/4.0 community and the lambda expressions lovers? Or is a rogue one trick maverick I shouldn't worry about?
This has poor interop. For example, consider this C# - F# example
C#:
public class Class1
{
public static void Foo(Func<object, string> f)
{
Console.WriteLine(f.Method.GetParameters()[0].Name);
}
}
F#:
Class1.Foo(fun yadda -> "hello")
Result:
"arg" is printed (not "yadda").
As a result, library designers should either avoid these kinds of 'abuses', or else at least provide a 'standard' overload (eg that takes the string name as an extra parameter) if they want to have good interop across .Net languages.
I find that odd not so much because of the name, but because the lambda is unnecessary ; it could use an anonymous-type and be more flexible:
.Attributes(new { style = "width:100%", @class="foo", blip=123 });
This is a pattern used in much of ASP.NET MVC (for example), and has other uses (a caveat, note also Ayende's thoughts if the name is a magic value rather than caller-specific)
Just wanted to throw in my opinion (I'm the author of the MvcContrib grid component).
This is definitely language abuse - no doubt about it. However, I wouldn't really consider it counter intuitive - when you look at a call to Attributes(style => "width:100%", @class => "foo")
I think it's pretty obvious what's going on (It's certainly no worse than the anonymous type approach). From an intellisense perspective, I agree it is pretty opaque.
For those interested, some background info on its use in MvcContrib...
I added this to the grid as a personal preference - I do not like the use of anonymous types as dictionaries (having a parameter that takes "object" is just as opaque as one that takes params Func[]) and the Dictionary collection initializer is rather verbose (I am also not a fan of verbose fluent interfaces, eg having to chain together multiple calls to an Attribute("style", "display:none").Attribute("class", "foo") etc)
If C# had a less verbose syntax for dictionary literals, then I wouldn't have bothered including this syntax in the grid component :)
I also want to point out that the use of this in MvcContrib is completely optional - these are extension methods that wrap overloads that take an IDictionary instead. I think it's important that if you provide a method like this you should also support a more 'normal' approach, eg for interop with other languages.
Also, someone mentioned the 'reflection overhead' and I just wanted to point out that there really isn't much of an overhead with this approach - there is no runtime reflection or expression compilation involved (see http://blog.bittercoder.com/PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx).
链接地址: http://www.djcxy.com/p/21208.html上一篇: 使用Java反射更改私有静态最终字段