Reflecting parameter name: abuse of C# lambda expressions or syntax brilliance? [closed]
Asked Answered
M

21

433

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. It 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 a sudden, I find code that actively looks and makes decisions 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?

Macguiness answered 11/11, 2009 at 21:0 Comment(26)
+1 for bringing the hashrocket to C#Node
change foreach (var func in hash) to: Array.ForEarch(hash, func => Add(func.Method.GetParameters()[0].Name, func(null)));Pinero
I would argue that this looks obvious, as long as you are willing to look at the intention of the code, rather than merely parsing syntax. Which, if you are reading good code, is what you should be doing anyway. Syntax is just a vehicle for intention, and I would argue that this is intention revealing code.Kraul
Were they former Perl users? There, style => "width:100%" is equivalent to "style", "width:100%" in every way.Jugendstil
@Eric It has gotta be bad when part of the compiler team makes a comment like that. Maybe ask Anders what he thinks?Kraul
I'm waiting for stackoverflow.com/users/8560 to step in and defend his code...Macguiness
This is just asking for trouble and unintended consequences.Jaxartes
I just asked Anders (and the rest of the design team) what they thought. Let's just say the results would not be printable in a family-friendly newspaper.Apperceive
@Eric Lippert: There are baudy saucy raunchy blue off color vile non family friendly newspapers? Where can I find these? In addition could you elaborate on the specifics of the horridness? I'd really like to hear why you guys think it's so bad...Attar
Sure. Seattle's is "The Stranger". As for why this is horrid, we could start with unobvious, clever (remember, clever is bad, clever code is hard to maintain), not at all within the by-design use cases envisaged by the designers of lambdas, slow, brittle, unportable, and unnecessary.Apperceive
C# is currently missing a clean, light syntax for maps, especially maps passed in to functions. Various dynamic languages (Ruby, Python, JavaScript, ColdFusion 9) have a clean, light syntax for that, to some extend or another.Eisinger
@Eric Thanks for asking. I have argued below that this is a nice looking syntax, but can understand your argument against it. Those are all valid reasons not to do something.Kraul
Oh, I think it LOOKS delightful. As for a syntax for maps, yeah, it would be great if we could do new { {"Do", "A deer" } , {"Re", "golden sun"} ... } and have the compiler infer the construction of a map, just as new[] {1, 2,3 } infers the construction of an int array. We're considering these sorts of things.Apperceive
What's a syntax map btw?Guntar
A "map" is what the .NET library calls a "dictionary" -- a device which "maps" a key (and the key can be one key, a pair of keys, whatever) onto a value. A great many concepts in programming are actually just a fancy way of doing a map. For example, you could think of someObject.SomeProperty as being a map from the pair (value of someObject, name of SomeProperty) to the value of the property. Because this idea is so fundamental, it is nice when a programming language provides a slick syntax for describing an arbitrary map. Collection initializers are a good start.Apperceive
Just blogged by K. Scott Allen - "Your Abomination Is My Clever Hack" odetocode.com/Blogs/scott/archive/2009/11/30/…Louisville
So I guess this is what happens when someone wants to use Rails but knows C#? Not everything you can do in RoR needs to be ported to other languages and frameworksLantern
I've tried to respond to some of the issues raised here on my blog: jeremyskinner.co.uk/2009/12/02/lambda-abuse-the-mvccontrib-hashEvvoia
Lambdas are going to become the regular expressions of .NET shrugMonaghan
Eric Lippert, I respect you greatly, but I think you and the group (including Anders, who I respect FREAKIN' TREMENDOUSLY) are slamming this too harshly. As you admit, C# lacks a tight syntax for maps, and some other langs (like Ruby) have great ones. This guy found a way to get the syntax he wanted. I'll grant you there are similar ways to express it that are almost as expressive as his syntax with fewer downsides. But his syntax and the fact he worked so hard to get it clearly shows a need for a language enhancement. Past performance indicates you guys will create something great for it.Xyloid
I wonder what happens when this is optimized a bit too much, or worse - obfuscated.Daph
I wouldn't hire anyone who either does this or doesn't immediately understand why it shouldn't be done.Hexastyle
I love C#, but it isn't the most expressive a language could possibly be. There are plenty of meta-programming concepts that C# can't express - yet we come upon problems whose solutions require or could greatly benefit from them. The CLR hides all kinds of magical abuses of the lower-levels, and some people complain about it too. But look what it enables us to do! Same with this lambda trick. It's a well rounded solution to an otherwise ugly C# expression, it seems well documented and it is obvious in it's usage/purpose visually, and it helps us create cool websites quickly.Woollyheaded
the anonymous type to IDictionary<string,object> would be a better choice IMHO. And it's used in a lot of places in asp.net MVC. A map type with simple init syntax would be very nice (similar syntax than anonymous types?) edit: didn't seen the Marc Gravell answer...Poree
who is contributing and contributor here? :)Sericeous
this is disqualified by the fact alone, that a valid html attribute name is not necessarily a valid c# identifier name, think of reserved keywords.Chukar
M
149

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 (e.g. that takes the string name as an extra parameter) if they want to have good interop across .Net languages.

Mold answered 11/11, 2009 at 21:10 Comment(11)
Something I had not considered. How would one access the expression tree correctly for it to work in the F# case?Kraul
You don't. This strategy is simply non-portable. (In case it helps, as an example the other way, F# could be capable of overloading methods that differ only in return type (type inference can do that). This can be expressed in the CLR. But F# disallows it, mostly because if you did that, those APIs would not be callable from C#.) When it comes to interop, there are always trade-offs at 'edge' features regarding what benefits you get versus what interop you trade away.Mold
That seems like a bit of an oversight on the F# language side -> is there a good reason why this is the case? I'm guessing lambda expressions in C# and functions in F# generate different IL.Kraul
I don't like non interoperability as a reason to not do something. If interoperability is a requirement then do it, if not, then why worry about it? This is YAGNI IMHO.Dannica
@jfar: In .NET CLR land interoperability has a whole new dimenssion, as assemblies generated in any compiler are supposed to be consumed from any other language.Macguiness
@Remus Rusanu: I thought it was okay to do non-interoperable things as long as you weren't claiming to be [CLSCompliant].Punctuate
I agree that you don't have to be CLS compliant, but it seems like a good idea if you are writing a library or control (and the snippet that start this is from a grid, yes?) Otherwise, you're just limiting your audience/customer baseGentleness
Maybe it's worth changing the: Func<object, string> to Expression<<Func<object, string>> And if you constrain the right hand side of the expression to just be constants you can have an implementation that does this: public static IDictionary<string, string> Hash(params Expression<Func<object, string>>[] hash) { Dictionary<string, string> values = new Dictionary<string,string>(); foreach (var func in hash) { values[func.Parameters[0].Name] = (string)((ConstantExpression)func.Body).Value; } return values; }Govern
@dfowler: so does func.Parameters[0].Name have better interop support than func.Method.GetParameters()[0].Name? Is that what you're saying?Gerta
I like the lambda syntax, but just today I discovered another problem with it. It won't work with the new HTML5 "data-" attributes, because something like data-key isn't a valid C# identifier. Good thing there's the overload to fall back on.Trousers
@Kyralessa You could also make an extension method of your own, specifically for the data attributes. Say, .Data(myField => whatever). Now you have something more clear than using Attribute - you are semantically trying to pass data, after all, not an attribute.Slob
H
153

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)

Hunchback answered 11/11, 2009 at 21:35 Comment(8)
This also has interop issues; not all languages support creating an anonymous type like that on the fly.Mold
I love how nobody really answered the question, instead people provide a "this is better" argument. :p Is it an abuse or not?Dorking
It's all about readability. This approach involves more braces and new keyword. Could you add some thoughts why this (using anonymous-type) is more flexible?Veratridine
I would be interested to see Eric Lippert's reaction to this. Because it's in FRAMEWORK code. And it's just as horrible.Hurtle
I don't think the problem is readability after the code has been written. I think the real issue is the learnability of the code. What are you going to think when your intellisense says .Attributes(object obj)? You'll have to go read the documentation (which nobody wants to do) because you won't know what to pass to the method. I don't think this is really any better than the example in the question.Albaugh
@Arnis - why more flexible: it isn't relying on the implied parameter name, which might (don't quote me) cause issues with some lambda implementations (other languages) - but you can also use regular objects with defined properties. For example, you could have a HtmlAttributes class with the expected attributes (for intellisense), and just ignore those with null values...Hunchback
(ah, see the F# example above for an illustration of the lambda implementation wossit - I knew I'd seen it somewhere!)Hunchback
Would be nice to be able to send directly anonymous objects to function(IDictionary x), a bit like the an Expression<Func<>> parameter in a function works for lambdasPoree
M
149

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 (e.g. that takes the string name as an extra parameter) if they want to have good interop across .Net languages.

Mold answered 11/11, 2009 at 21:10 Comment(11)
Something I had not considered. How would one access the expression tree correctly for it to work in the F# case?Kraul
You don't. This strategy is simply non-portable. (In case it helps, as an example the other way, F# could be capable of overloading methods that differ only in return type (type inference can do that). This can be expressed in the CLR. But F# disallows it, mostly because if you did that, those APIs would not be callable from C#.) When it comes to interop, there are always trade-offs at 'edge' features regarding what benefits you get versus what interop you trade away.Mold
That seems like a bit of an oversight on the F# language side -> is there a good reason why this is the case? I'm guessing lambda expressions in C# and functions in F# generate different IL.Kraul
I don't like non interoperability as a reason to not do something. If interoperability is a requirement then do it, if not, then why worry about it? This is YAGNI IMHO.Dannica
@jfar: In .NET CLR land interoperability has a whole new dimenssion, as assemblies generated in any compiler are supposed to be consumed from any other language.Macguiness
@Remus Rusanu: I thought it was okay to do non-interoperable things as long as you weren't claiming to be [CLSCompliant].Punctuate
I agree that you don't have to be CLS compliant, but it seems like a good idea if you are writing a library or control (and the snippet that start this is from a grid, yes?) Otherwise, you're just limiting your audience/customer baseGentleness
Maybe it's worth changing the: Func<object, string> to Expression<<Func<object, string>> And if you constrain the right hand side of the expression to just be constants you can have an implementation that does this: public static IDictionary<string, string> Hash(params Expression<Func<object, string>>[] hash) { Dictionary<string, string> values = new Dictionary<string,string>(); foreach (var func in hash) { values[func.Parameters[0].Name] = (string)((ConstantExpression)func.Body).Value; } return values; }Govern
@dfowler: so does func.Parameters[0].Name have better interop support than func.Method.GetParameters()[0].Name? Is that what you're saying?Gerta
I like the lambda syntax, but just today I discovered another problem with it. It won't work with the new HTML5 "data-" attributes, because something like data-key isn't a valid C# identifier. Good thing there's the overload to fall back on.Trousers
@Kyralessa You could also make an extension method of your own, specifically for the data attributes. Say, .Data(myField => whatever). Now you have something more clear than using Attribute - you are semantically trying to pass data, after all, not an attribute.Slob
E
137

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

Evvoia answered 11/11, 2009 at 21:0 Comment(3)
I've also tried to address some of the issues raised here in some more depth on my blog: jeremyskinner.co.uk/2009/12/02/lambda-abuse-the-mvccontrib-hashEvvoia
It's no less opaque in Intellisense than anonymous objects.Shrapnel
+1 for mentioning that the interface is added via optional extension methods. Non C# users (and anyone offended by the language abuse) can simply refrain using it.Region
A
49

I would prefer

Attributes.Add(string name, string value);

It's much more explicit and standard and nothing is being gained by using lambdas.

Albaugh answered 11/11, 2009 at 21:6 Comment(8)
Is it though? html.Attributes.Add("style", "width:100%"); doesn't read as nicely as style = "width:100%" (the actual html generated), whereas style => "width:100%" is very close to what it looks like in the resulting HTML.Kraul
Their syntax allows for tricks like .Attributes(id=>'foo', @class=>'bar', style=>'width:100%'). The function signature uses the params syntax for variable number of args: Attributes(params Func<object, object>[] args). It is very powerfull, but it took me quite a while to understand wtf it does.Macguiness
@Jamie: Trying to make the C# code look like the HTML code would be a bad reason for design descisions. They are completely different languages for completely different purposes, and they should not look the same.Harkins
An anonymous object might just as well have been used, without sacrificing the "beauty"? .Attributes(new {id = "foo", @class = "bar", style = "width:100%"}) ??Lawlor
@Harkins Why would it be a bad reason for design decisions? Why should they not look the same? By that reasoning should they intentionally look different? I'm not saying your wrong, I'm just saying you might want to more fully elaborate your point.Mcgraw
@Stuart: Making code that output HTML look like HTML is like making code that prints bar codes look like bar codes... The way code is written should not be influenced by what the output looks like. Making the code intentionally look different doesn't really apply here, but in ASP code I have intentionally exaggerated the differences between VBScript and Javascript to make them easy to recognise.Harkins
I completely agree, but this does not answer the question at all.Dorking
@Guffa, by that reasoning, you'd never use anything like ASP.NET, ASP.NET MVC, JSP, or any similar web technology. The reason they exist is to let your view code look as close as possible to html. We already had servlets before any of that came out, and a servlet is merely a method that generates textual html from code. The world wanted something that looked more like the output that was being generated, and apparently you did too since you mention ASP.Xyloid
A
45

Welcome To Rails Land :)

There is really nothing wrong with it as long as you know what's going on. (It's when this kind of thing isn't documented well that there is a problem).

The entirety of the Rails framework is built on the idea of convention over configuration. Naming things a certain way keys you into a convention they're using and you get a whole lot of functionality for free. Following the naming convention gets you where you're going faster. The whole thing works brilliantly.

Another place where I've seen a trick like this is in method call assertions in Moq. You pass in a lambda, but the lambda is never executed. They just use the expression to make sure that the method call happened and throw an exception if not.

Attar answered 11/11, 2009 at 21:9 Comment(2)
I was a bit hesitant to, but I agree. Aside from the reflection overhead, there is no significant difference between using a string as in Add() vs. using a lambda parameter name. At least that I can think of. You can muck it up and type "sytle" without noticing both ways.Mcgraw
I couldn't figure out why this wasn't weird to me, and then I remembered Rails. :DBulgaria
D
42

This is horrible on more than one level. And no, this is nothing like Ruby. It's an abuse of C# and .NET.

There have been many suggestions of how to do this in a more straightforward way: tuples, anonymous types, a fluent interface and so on.

What makes it so bad is that its just way to fancy for its own good:

  • What happens when you need to call this from Visual Basic?

    .Attributes(Function(style) "width:100%")

  • It's completely counter intuitive, and intellisense will provide little help figuring out how to pass stuff in.

  • It's unnecessarily inefficient.

  • Nobody will have any clue how to maintain it.

  • What is the type of the argument going in to attributes? is it Func<object,string>? How is that intention revealing? What is your intellisense documentation going to say, "Please disregard all values of object"?

I think you are completely justified having those feelings of revulsion.

Dorking answered 11/11, 2009 at 23:48 Comment(4)
I would say - it's completely intuitive. :)Veratridine
You say it is nothing like Ruby. But it is an awful heck of a lot like Ruby's syntax for specifying the keys and values of a hashtable.Xyloid
Code that breaks under alpha-conversion! Yey!Leslee
@Charlie, syntactically it looks similar, semantically it is way different.Dorking
D
38

I'm in the "syntax brilliance" camp, if they document it clearly, and it looks this freaking cool, there's almost no problem with it imo!

Damalas answered 11/11, 2009 at 21:7 Comment(2)
Amen, brother. Amen (2nd amen required to meet min length for comments :)Xyloid
Your comment alone was way more than needed. But then, you can't just Amen once and then put in your comment :DLiddy
V
37

Both of them. It's abusage of lambda expressions AND syntax brilliance.

Veratridine answered 11/11, 2009 at 21:0 Comment(1)
So it's a brilliant syntactical abuse of lambda expressions? I think I agree :)Alcala
B
21

I hardly ever came across this kind of usage. I think it's "inappropriate" :)

This is not a common way of use, it is inconsistent with the general conventions. This kind of syntax has pros and cons of course:

Cons

  • The code is not intuitive (usual conventions are different)
  • It tends to be fragile (rename of parameter will break the functionality).
  • It's a little more difficult to test (faking the API will require usage of reflection in tests).
  • If expression is used intensively it'll be slower due to the need to analyze the parameter and not just the value (reflection cost)

Pros

  • It's more readable after the developer adjusted to this syntax.

Bottom line - in public API design I would have chosen more explicit way.

Brinn answered 11/11, 2009 at 21:12 Comment(2)
@Brinn - your Pros and Cons are reversed. At least I hope you're not saying that a Pro is having code "not intuitive". ;-)Epoxy
For this particular case - lambda parameter name and string parameter are both fragile. It's like using dynamic for xml parsing - it's appropriate because you can't be sure about xml anyway.Veratridine
H
19

No, it's certainly not common practice. It's counter-intuitive, there is no way of just looking at the code to figure out what it does. You have to know how it's used to understand how it's used.

Instead of supplying attributes using an array of delegates, chaining methods would be clearer and perform better:

.Attribute("style", "width:100%;").Attribute("class", "test")

Although this is a bit more to type, it's clear and intuitive.

Harkins answered 11/11, 2009 at 21:13 Comment(5)
Really? I knew exactly what that snippet of code intended when I looked at it. It's not that obtuse unless you're very strict. One could give the same argument about overloading + for string concatenation, and that we should always use a Concat() method instead.Mcgraw
@Stuart: No, you didn't know exactly, you were just guessing based on the values used. Anyone can make that guess, but guessing is not a good way of understanding code.Harkins
I'm guessing using .Attribute("style", "width:100%") gives me style="width:100%", but for all I know it could give me foooooo. I'm not seeing the difference.Mcgraw
"Guessing based on the values used" is ALWAYS what you do when looking at code. If you encounter a call to stream.close() you assume it closes a stream, yet it might as well do something completely different.Platitudinous
@Wouter: If you are ALWAYS guessing when reading code, you must have great difficulties reading code... If I see a call to a method named "close" I can gather that the author of the class doesn't know about naming conventions, so I would be very hesitant to take anything at all for granted about what the method does.Harkins
P
18

Can I use this to coin a phrase?

magic lambda (n): a lambda function used solely for the purpose of replacing a magic string.

Priapus answered 11/11, 2009 at 21:0 Comment(1)
yeah... that's funny. and maybe it's magical kinda, in the sense of no compile time safety, is there a place where this usage would cause runtime instead of compile-time errors?Moselle
C
17

What's wrong with the following:

html.Attributes["style"] = "width:100%";
Cementum answered 11/11, 2009 at 21:0 Comment(0)
X
16

All this ranting about "horridness" is a bunch of long-time c# guys overreacting (and I'm a long-time C# programmer and still a very big fan of the language). There's nothing horrible about this syntax. It is merely an attempt to make the syntax look more like what you're trying to express. The less "noise" there is in the syntax for something, the easier the programmer can understand it. Decreasing the noise in one line of code only helps a little, but let that build up across more and more code, and it turns out to be a substantial benefit.

This is an attempt by the author to strive for the same benefits that DSL's give you -- when the code just "looks like" what you're trying to say, you've reached a magical place. You can debate whether this is good for interop, or whether it is enough nicer than anonymous methods to justify some of the "complexity" cost. Fair enough ... so in your project you should make the right choice of whether to use this kind of syntax. But still ... this is a clever attempt by a programmer to do what, at the end of the day, we're all trying to do (whether we realize it or not). And what we're all trying to do, is this: "Tell the computer what we want it to do in language that is as close as possible to how we think about what want it to do."

Getting closer to expressing our instructions to computers in the same manner that we think internally is a key to making software more maintainable and more accurate.

EDIT: I had said "the key to making software more maintainable and more accurate", which is a crazily naive overstated bit of unicorniness. I changed it to "a key."

Xyloid answered 11/11, 2009 at 21:0 Comment(0)
K
12

This is one of the benefits of expression trees - one can examine the code itself for extra information. That is how .Where(e => e.Name == "Jamie") can be converted into the equivalent SQL Where clause. This is a clever use of expression trees, though I would hope that it does not go any further than this. Anything more complex is likely to be more difficult than the code it hopes to replace, so I suspect it will be self limiting.

Kraul answered 11/11, 2009 at 21:6 Comment(2)
Is a valid point, but truth in advertising: LINQ comes with a whole set of attributes like TableAttribute and ColumnAttribute that make this a more legit affair. Also linq mapping looks at class names and property names, which are arguably more stable than a parameter names.Macguiness
I agree with you there. I've changed my stance on this slightly after reading what Eric Lippert/Anders Helsberg/etc had to say on the matter. Thought I'd leave this answer up as it is still somewhat helpful. For what it's worth, I now think this style of working with HTML is nice, but it doesn't fit with the language.Kraul
E
8

It is an interesting approach. If you constrained the right hand side of the expression to be constants only then you could implementing using

Expression<Func<object, string>>

Which I think is what you really want instead of the delegate (you're using the lambda to get names of both sides) See naive implementation below:

public static IDictionary<string, string> Hash(params Expression<Func<object, string>>[] hash) {
    Dictionary<string, string> values = new Dictionary<string,string>();
    foreach (var func in hash) {
        values[func.Parameters[0].Name] = ((ConstantExpression)func.Body).Value.ToString();
    }
    return values;
}

This might even address the cross language interop concern that was mentioned earlier in the thread.

Evelyn answered 11/11, 2009 at 21:0 Comment(0)
P
6

The code is very clever, but it potentially causes more problems that it solves.

As you've pointed out, there's now an obscure dependency between the parameter name (style) and an HTML attribute. No compile time checking is done. If the parameter name is mistyped, the page probably won't have a runtime error message, but a much harder to find logic bug (no error, but incorrect behavior).

A better solution would be to have a data member that can be checked at compile time. So instead of this:

.Attributes(style => "width:100%");

code with a Style property could be checked by the compiler:

.Attributes.Style = "width:100%";

or even:

.Attributes.Style.Width.Percent = 100;

That's more work for the authors of the code, but this approach takes advantage of C#'s strong type checking ability, which helps prevent bugs from getting into code in the first place.

Pademelon answered 11/11, 2009 at 21:23 Comment(1)
I appreciate compile-time checking, but I think this comes down to a matter of opinion. Maybe something like new Attributes() { Style: "width:100%" } would win more people over to this, since it's more terse. Still, implementing everything HTML allows is a huge job and I can't blame someone for just using strings/lambdas/anonymous classes.Mcgraw
S
5

indeed its seems like Ruby =), at least for me the use of a static resource for a later dynamic "lookup" doesn't fit for api design considerations, hope this clever trick is optional in that api.

We could inherit from IDictionary (or not) and provide an indexer that behaves like a php array when you dont need to add a key to set a value. It will be a valid use of .net semantics not just c#, and still need documentation.

hope this helps

Solicitous answered 11/11, 2009 at 22:30 Comment(0)
A
4

IMHO, it is a cool way of doing it. We all love the fact that naming a class Controller will make it a controller in MVC right? So there are cases where the naming does matter.

Also the intention is very clear here. It is very easy to understand that .Attribute( book => "something") will result in book="something" and .Attribute( log => "something") will result in log="something"

I guess it should not be a problem if you treat it like a convention. I am of the opinion that whatever makes you write less code and makes the intention obvious is a good thing.

Aubin answered 11/11, 2009 at 21:0 Comment(1)
Naming a class Controller won't do squat though if you don't inherit from controller too...Greaser
E
4

In my opinion it is abuse of the lambdas.

As to syntax brilliance i find style=>"width:100%" plain confusing. Particularily because of the => instead of =

Elasticize answered 11/11, 2009 at 21:20 Comment(0)
O
3

If the method (func) names are well chosen, then this is a brilliant way to avoid maintenance headaches (ie: add a new func, but forgot to add it to the function-parameter mapping list). Of course, you need to document it heavily and you'd better be auto-generating the documentation for the parameters from the documentation for the functions in that class...

Occupy answered 11/11, 2009 at 21:18 Comment(0)
R
1

I think this is no better than "magic strings". I'm not much of a fan of the anonymous types either for this. It needs a better & strongly typed approach.

Rationale answered 11/11, 2009 at 21:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.