我看着MvcContrib网格组件,我很着迷,但同时又被网格语法中使用的语法技巧所排斥:

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

上面的语法将生成的HTML的样式属性设置为width:100%。如果你注意的话,'style'并没有被指定。它是从表达式中参数的名称推导出来的!我必须深入研究这个问题,找到“魔法”发生的地方:

Hash(params Func<object, TValue>[] hash)
{
    foreach (var func in hash)
    {
        Add(func.Method.GetParameters()[0].Name, func(null));
    }
}

因此,代码确实使用了正式的、编译时的参数名称来创建属性名称-值对的字典。生成的语法结构确实非常具有表现力,但同时也非常危险。

lambda表达式的一般用法允许替换所使用的名称而不产生副作用。我在一本书上看到一个例子,上面写着收藏。我知道我可以在我的代码集合中写。ForEach(log => fire。burn (log))它的意思是一样的。但是使用这里的MvcContrib Grid语法,我突然发现代码可以根据我为变量选择的名称主动查找并做出决定!

那么这是c# 3.5/4.0社区和lambda表达式爱好者的普遍做法吗?还是说,我不应该担心一个流氓的诡计特立独行?


当前回答

我认为这不比“魔术弦”好多少。在这方面,我也不太喜欢匿名类型。它需要一种更好的强类型方法。

其他回答

不,这当然不是常见的做法。这是违反直觉的,没有办法只看代码来弄清楚它是做什么的。你必须知道它是如何被使用的才能理解它是如何被使用的。

而不是使用委托数组提供属性,链接方法将更清晰,性能更好:

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

虽然输入起来有点费劲,但它清晰直观。

欢迎来到Rails Land:)

只要你知道发生了什么,它就没有什么问题。(当这类事情没有很好地记录时,就会出现问题)。

整个Rails框架是建立在约定优于配置的思想之上的。以特定的方式命名事物将您带入他们正在使用的约定,并且您可以免费获得大量功能。遵循命名惯例可以让你更快地到达目的地。整个系统运作得非常出色。

我还在Moq中的方法调用断言中看到过类似的技巧。你传入一个lambda,但是lambda永远不会被执行。它们只是使用表达式来确保方法调用发生了,如果没有发生则抛出异常。

只是想提出我的观点(我是MvcContrib网格组件的作者)。

毫无疑问,这绝对是语言滥用。然而,我不会真的认为这是反直觉的-当你看一个属性的调用(style => "width:100%", @class => "foo") 我认为这是非常明显的(它当然不比匿名类型方法更糟糕)。从智能感知的角度来看,我同意这是相当不透明的。

对于那些感兴趣的人,它在MvcContrib中使用的一些背景信息…

我把它添加到网格作为个人偏好-我不喜欢使用匿名类型作为字典(有一个接受“object”的参数就像一个接受参数Func[]一样不透明)和字典集合初始化器相当冗长(我也不喜欢冗长的流畅接口,例如必须将多个调用链接到一个属性(“style”,“display:none”)。属性("class", "foo")等)

如果c#对字典字面量有一个不那么冗长的语法,那么我就不会在网格组件中包含这个语法了:)

我还想指出,在MvcContrib中使用这个是完全可选的-这些是扩展方法,包装重载,以字典代替。我认为重要的是,如果你提供了这样的方法,你也应该支持更“正常”的方法,例如与其他语言的互操作。

另外,有人提到了“反射开销”,我只是想指出,这种方法真的没有太多的开销——没有运行时反射或表达式编译(参见http://blog.bittercoder.com/PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx)。

我更喜欢

Attributes.Add(string name, string value);

它更加明确和标准,使用lambdas什么也得不到。

这是表达式树的好处之一——可以检查代码本身以获得额外的信息。这就是如何将.Where(e => e. name == "Jamie")转换为等效的SQL Where子句。这是表达式树的一种巧妙使用,尽管我希望它不会超出这个范围。任何更复杂的代码都可能比它希望取代的代码更难,因此我怀疑它将是自我限制的。