我正在看MvcContrib网格组件,我很着迷,但同时被Grid syntax中使用的句法技巧击退了:
.Attributes(style => "width:100%")
上面的语法将生成的HTML的style属性设置为width:100%
。现在,如果你注意,“风格”没有指定,是从表达式中参数的名称推断出来的!我不得不深入研究这个并发现“神奇”发生的地方:
Hash(params Func<object, TValue>[] hash)
{
foreach (var func in hash)
{
Add(func.Method.GetParameters()[0].Name, func(null));
}
}
确实,代码使用正式的编译时,参数名来创建属性名称 - 值对的字典。结果语法结构确实非常具有表现力,但同时也非常危险。 lambda表达式的一般用法允许替换没有副作用的名称。我在一本书中看到一个例子,collection.ForEach(book => Fire.Burn(book))
我知道我可以在我的代码collection.ForEach(log => Fire.Burn(log))
和中写出同样的东西。但是,突然间有MvcContrib网格语法,我发现代码主动查找并根据我为变量选择的名称做出决定!
这是C#3.5 / 4.0社区和lambda表达爱好者的常见做法吗?或者是一个我不应该担心的流氓特立独行?
答案 0 :(得分:154)
由于名称,我觉得这很奇怪,但因为 lambda是不必要的;它可以使用匿名类型并且更灵活:
.Attributes(new { style = "width:100%", @class="foo", blip=123 });
这是在ASP.NET MVC的大部分内容中使用的模式(例如),并且有other uses(一个caveat,如果名称是魔术值,请注意Ayende's thoughts比来电专用)
答案 1 :(得分:146)
这种互操作性很差。例如,考虑这个C# - F#示例
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")
结果:
打印“arg”(不是“yadda”)。
因此,库设计者应该避免这种“滥用”,或者至少提供“标准”重载(例如,将字符串名称作为额外参数),如果他们想要具有良好的互操作性。网络语言。
答案 2 :(得分:137)
只是想提一下(我是MvcContrib网格组件的作者)。
这绝对是语言滥用 - 毫无疑问。但是,当你看到Attributes(style => "width:100%", @class => "foo")
的电话时,我真的不会认为这是违反直觉的
我认为这很明显是什么(它肯定不比匿名类型方法更糟)。从智能理论的角度来看,我同意这是非常不透明的。
对于那些感兴趣的人,在MvcContrib中使用它的一些背景信息...
我将此作为个人偏好添加到网格中 - 我不喜欢使用匿名类型作为词典(使用带有“object”的参数与使用params Func [])和词典的参数一样不透明集合初始化程序相当冗长(我也不喜欢详细的流畅接口,例如必须将多个调用链接到一个Attribute(“style”,“display:none”)。属性(“class”,“foo”)等)
如果C#的字典文字语法较为冗长,那么我就不会在网格组件中包含这种语法:)
我还想指出在MvcContrib中使用它是完全可选的 - 这些是包含带有IDictionary的重载的扩展方法。我认为重要的是,如果您提供这样的方法,您还应该支持更“正常”的方法,例如与其他语言互操作。
另外,有人提到'反射开销',我只是想指出这种方法确实没有太大的开销 - 没有涉及运行时反射或表达式编译(参见http://blog.bittercoder.com/PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx)
答案 3 :(得分:48)
我更喜欢
Attributes.Add(string name, string value);
它更明确,更标准,使用lambdas没有任何好处。
答案 4 :(得分:46)
欢迎来到Rails Land:)
只要你知道发生了什么,它就没有什么问题。 (当这种事情没有很好地证明存在问题时)。
整个Rails框架都建立在约定优于配置的基础之上。以某种方式命名事物可以将您锁定到他们正在使用的约定中,并且您可以免费获得大量功能。遵循命名约定可以让您更快地到达目的地。整个过程非常出色。
我见过这样一个技巧的另一个地方是Moq中的方法调用断言。你传入一个lambda,但lambda永远不会被执行。他们只是使用表达式来确保方法调用发生,如果没有则抛出异常。
答案 5 :(得分:42)
在多个级别上,这是可怕的。不,这与Ruby不同。这是对C#和.Net的滥用。
有很多关于如何以更直接的方式做到这一点的建议:元组,匿名类型,流畅的界面等等。
让它变得如此糟糕的原因在于它只是为了自己的利益而着想:
当你需要从VB调用它时会发生什么?
.Attributes(Function(style) "width:100%")
它完全反直觉,智能感知将无法帮助确定如何传递内容。
它不必要地效率低下。
没有人会知道如何维护它。
参与属性的参数类型是Func<object,string>
吗?这个意图是如何揭示的。您的智力感知文档会说什么,“请忽略对象的所有值”
我认为你有这种反感的感觉是完全合理的。
答案 6 :(得分:40)
我正处于“语法上的光彩”阵营,如果他们清楚地记录下来,看起来很酷,它几乎没有问题!
答案 7 :(得分:37)
他们两个。它是lambda表达式 AND 语法亮度的滥用。
答案 8 :(得分:21)
我几乎没有遇到过这种用法。我认为这是“不恰当的”:)。
这不是一种常用的使用方式,它与一般惯例不一致。这种语法当然有利有弊:
<强>缺点强>
<强>赞成强>
底线 - 在公共API设计中,我会选择更明确的方式。
答案 9 :(得分:18)
不,这当然不常见。这是违反直觉的,没有办法只看代码来弄清楚它的作用。你必须知道如何使用它来理解它是如何被使用的。
链接方法不是使用委托数组提供属性,而是更清晰,性能更好:
.Attribute("style", "width:100%;").Attribute("class", "test")
虽然输入的内容多一点,但它清晰直观。
答案 10 :(得分:17)
所有这些关于“可怕”的咆哮都是一群长期反对的c#家伙(而且我是一名长期的C#程序员,而且仍然是该语言的忠实粉丝)。这种语法没什么可怕的。它只是试图使语法看起来更像你想要表达的内容。语法中的“噪音”越少,程序员就越容易理解它。减少一行代码中的噪声只会有所帮助,但是让它在越来越多的代码中积累,结果证明是一个重大的好处。
这是作者试图争取DSL给你的同样好处 - 当代码只是“看起来像”你想说的时候,你已经到了一个神奇的地方。您可以讨论这是否适用于互操作,或者它是否比匿名方法更好,以证明某些“复杂性”成本。足够公平......所以在你的项目中你应该正确选择是否使用这种语法。但仍然......这是一个程序员做一些聪明的尝试,在一天结束时,我们都在尝试做(无论我们是否意识到)。而我们所有人都想做的是:“告诉计算机我们希望用尽可能接近我们想要做什么的语言做什么。”
更接近于以与我们内部思考相同的方式向计算机表达我们的指示,这是使软件更易于维护和更准确的关键。
编辑:我曾说过“使软件更易于维护和更准确的关键”,这是一种疯狂的过分夸大的独特性。我把它改成了“一把钥匙。”答案 11 :(得分:17)
以下内容有什么问题:
html.Attributes["style"] = "width:100%";
答案 12 :(得分:17)
我可以用它来拼写短语吗?
magic lambda(n):一个lambda函数,仅用于替换魔术字符串。
答案 13 :(得分:12)
这是表达式树的一个好处 - 可以检查代码本身以获取额外信息。这就是.Where(e => e.Name == "Jamie")
可以转换为等效的SQL Where子句的方式。这是表达树的巧妙使用,但我希望它不会比这更进一步。任何更复杂的东西都可能比它希望取代的代码更难,所以我怀疑它会自我限制。
答案 14 :(得分:7)
这是一种有趣的方法。如果您将表达式的右侧约束为常量,那么您可以使用
实现Expression<Func<object, string>>
我认为你真正想要的不是代表(你使用lambda来获取双方的名字) 请参阅下面的天真实现:
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;
}
这甚至可能解决了之前在线程中提到的跨语言互操作问题。
答案 15 :(得分:6)
代码非常聪明,但它可能会导致更多问题解决。
正如您所指出的,现在参数名称(样式)和HTML属性之间存在模糊的依赖关系。没有编译时间检查。如果参数名称输入错误,页面可能不会有运行时错误消息,但更难找到逻辑错误(没有错误,但行为不正确)。
更好的解决方案是拥有一个可以在编译时检查的数据成员。所以不要这样:
.Attributes(style => "width:100%");
编译器可以检查带有Style属性的代码:
.Attributes.Style = "width:100%";
甚至:
.Attributes.Style.Width.Percent = 100;
这对代码的作者来说更有用,但是这种方法利用了C#强大的类型检查功能,这有助于防止错误首先进入代码。
答案 16 :(得分:5)
确实它看起来像Ruby =),至少对我来说,使用静态资源进行后续的动态“查找”并不适合api设计考虑,希望这个聪明的技巧在api中是可选的。
我们可以继承IDictionary(或者不是)并提供一个索引器,当你不需要添加一个键来设置一个值时,它就像一个php数组。它将是.net语义的有效使用,不仅仅是c#,还需要文档。
希望这会有所帮助
答案 17 :(得分:5)
此处的意图非常明确。很容易理解,.Attribute( book => "something")
会导致book="something"
而.Attribute( log => "something")
会导致log="something"
如果你把它当成一种约定,我想它应该不是问题。我认为无论是什么让你编写更少的代码并使意图明显是一件好事。
答案 18 :(得分:4)
在我看来,这是对lambdas的滥用。
至于语法上的光彩,我发现style=>"width:100%"
简直令人困惑。特别是因为=>
而不是=
答案 19 :(得分:3)
如果方法(func)名称被很好地选择,那么这是避免维护麻烦的一种很好的方法(即:添加新的func,但忘记将其添加到函数参数映射列表中)。当然,你需要大量记录它,你最好自动生成该类函数文档中的参数文档......
答案 20 :(得分:1)
我认为这并不比“魔术字符串”好。对于这个,我不太喜欢匿名类型。它需要一个更好的&amp;强类型方法。