反映参数名称:滥用C#lambdaexpression式或语法辉煌?
我正在研究MvcContrib Grid组件,我着迷于它,但同时又被网格语法中使用的语法技巧击退了:
.Attributes(style => "width:100%")
上面的语法将生成的HTML的样式属性设置为width:100%
。 现在,如果你注意,'风格'是无处指定的,从expression式中参数的名称推导! 我不得不深究这一点,发现“魔法”发生的地方:
Hash(params Func<object, TValue>[] hash) { foreach (var func in hash) { Add(func.Method.GetParameters()[0].Name, func(null)); } }
所以确实,代码使用forms,编译时间,参数名称来创build属性名称 – 值对的字典。 由此产生的语法结构的确非常具有performance力,但同时又非常危险。 一般使用lambdaexpression式可以replace所使用的名称而不会产生副作用。 我在书中看到一个例子,说collection.ForEach(book => Fire.Burn(book))
我知道我可以写在我的代码collection.ForEach(log => Fire.Burn(log))
, 它意味着相同东西 。 但是,在这里使用MvcContrib Grid语法突然间,我发现代码可以根据我为我的variablesselect的名称进行主动查找并进行分解。
那么C#3.5 / 4.0社区和lambdaexpression式爱好者的这种常见做法是什么? 或者是一个stream氓特技,我不应该担心?
互操作性差。 例如,考虑这个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”)。
因此,图书馆devise者应该避免这种“滥用”,否则,如果他们想跨越.Net语言进行良好的互操作,至less应该提供一个“标准的”重载(例如,把string名称作为额外的参数)。
我觉得不是因为这个名字 ,而是因为lambda是不必要的 。 它可以使用匿名types并更灵活:
.Attributes(new { style = "width:100%", @class="foo", blip=123 });
这是一个ASP.NET MVC(例如)中使用的模式,并有其他用途 (一个警告 ,注意Ayende的想法,如果名称是一个神奇的价值,而不是调用者特定的)
只是想抛出我的意见(我是MvcContrib网格组件的作者)。
这绝对是语言滥用 – 毫无疑问。 然而,我并不认为这是直观的 – 当你看一个对Attributes(style => "width:100%", @class => "foo")
的调用Attributes(style => "width:100%", @class => "foo")
我认为很明显发生了什么事情(这肯定不比匿名types的方法更糟糕)。 从intellisense的angular度来看,我认为这是非常不透明的。
对于那些感兴趣的,一些关于它在MvcContrib中使用的背景信息…
我把它作为一个个人偏好添加到网格中 – 我不喜欢使用匿名types作为字典(有一个参数需要“对象”与使用params Func []一样不透明),Dictionary集合初始化程序是(我也不是一个详细的stream利接口的粉丝,例如必须将多个调用链接到一个属性(“style”,“display:none”)。属性(“class”,“foo”)等)
如果C#对于字典文字有一个不太冗长的语法,那么我不会在网格组件中包含这个语法。
我也想指出,在MvcContrib中的使用是完全可选的 – 这些是扩展方法,它们包装带有IDictionary的重载。 我认为重要的是,如果您提供这样的方法,您还应该支持更“常规”的方法,例如与其他语言交互。
另外,有人提到了“reflection开销”,我只想指出,这种方法确实没有太多的开销 – 没有运行时reflection或expression式编译(参见http://blog.bittercoder.com / PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx )。
我会比较喜欢
Attributes.Add(string name, string value);
它更加明确和标准,使用lambdas没有任何收获。
欢迎来到Rails土地:)
只要你知道发生了什么事情,那真的没什么问题。 (当这种事情没有被logging的时候,是有问题的)。
整个Rails框架是build立在约定优于configuration的基础上的。 以某种方式命名事物会将您locking到他们正在使用的约定中,并免费获得大量function。 遵循命名约定可以让你更快地到达。 整个事情出色地工作。
另一个我见过这种技巧的地方是Moq中的方法调用断言。 你传入一个lambda,但lambda永远不会被执行。 他们只是使用expression式来确保方法调用发生,如果不是,则抛出exception。
这在多个层面上是可怕的 。 不,这不像Ruby。 这是对C#和.Net的滥用。
有很多关于如何以更直接的方式来做到这一点的build议:元组,匿名types,stream畅的接口等等。
是什么让它变得如此糟糕才是为了自己的利益而想的唯一方法:
-
当你需要从VB调用这个时会发生什么?
.Attributes(Function(style) "width:100%")
-
它完全反直觉,intellisense将提供一点帮助搞清楚如何通过东西。
-
它不必要的低效率。
-
没有人会知道如何维护它。
-
什么是参数进入属性的types,是
Func<object,string>
? 这个意图如何揭示? 你的智能文档会说什么:“请忽视所有对象的价值”
我觉得你完全有理由感到这种反感。
我在“语法辉煌”的阵营,如果他们清楚地logging下来,而且看起来这很酷,那么它几乎没有问题!
两个都。 这是lambdaexpression式和语法辉煌的滥用。
我几乎没有遇到过这种用法。 我认为这是“不恰当的”:)
这不是一个常用的使用方式,与一般惯例不一致。 这种语法当然有利有弊:
缺点
- 代码不直观(通常惯例是不同的)
- 它往往是脆弱的(参数重命名将打破function)。
- testing起来要困难一些(伪造API需要在testing中使用reflection)。
- 如果expression式集中使用,由于需要分析参数,而不仅仅是数值(reflection成本)
优点
- 开发者调整到这个语法之后,它更易读。
底线 – 在公共APIdevise中,我会select更明确的方式。
不,这当然不是普遍的做法。 这是违反直觉的,没有办法只是看代码来找出它的作用。 你必须知道它是如何用来了解它是如何使用的。
而不是使用委托数组来提供属性,链接方法会更清晰,性能更好:
.Attribute("style", "width:100%;").Attribute("class", "test")
虽然这是更多types,它是清晰和直观的。
所有关于“可怕”的咆哮都是一群长期以来对c#的反应过度的人(我是一个长期的C#程序员,仍然是这个语言的忠实粉丝)。 这个语法没有什么可怕的。 这只是一个尝试,使语法看起来更像你想expression的东西。 语法上的“噪音”越less,程序员就越容易理解它。 减less一行代码中的噪音只会有所帮助,但是让它们在越来越多的代码中build立起来,结果是一个很大的好处。
这是作者努力争取DSL给你的同样好处 – 当代码只是“看起来”你想说什么,你已经达到了一个神奇的地方。 你可以辩论这是否适合互操作,或者它是否比匿名方法更好,以certificate某些“复杂性”成本。 够公平了…所以在你的项目中,你应该做出是否使用这种语法的正确select。 但是,这仍然是程序员做出的一个聪明的尝试,在一天结束的时候,我们都试图去做(无论我们是否意识到)。 而我们都试图做的是这样的:“告诉计算机我们希望它做的语言尽可能接近我们想要做的事情。”
越来越接近以我们内部认为的方式向计算机expression我们的指令是使软件更易维护和更准确的关键。
编辑:我曾经说过“使软件更易维护,更精确”的关键,这是一个疯狂的过分夸张unicorniness一点点。 我把它改成“一把钥匙”。
以下是什么问题:
html.Attributes["style"] = "width:100%";
我可以用这个来造句吗?
magic lambda(n):仅用于replace魔术串的lambda函数。
这是expression式树的好处之一 – 可以检查代码本身的额外信息。 那就是如何.Where(e => e.Name == "Jamie")
可以转换成等效的SQL Where子句。 这是对expression树的巧妙运用,尽pipe我希望它不会超出这个范围。 任何更复杂的事情可能比它希望replace的代码更难,所以我怀疑它会是自我限制的。
这是一个有趣的方法。 如果您将expression式的右侧限制为常量,那么您可以实现使用
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; }
这甚至可以解决在线程中提到的跨语言互操作性问题。
代码非常聪明,但它可能会导致更多的问题解决。
正如您已经指出的那样,现在参数名称(样式)和HTML属性之间存在一个模糊的依赖关系。 没有编译时间检查完成。 如果参数名称错误,页面可能不会有运行时错误消息,但更难find逻辑错误(没有错误,但行为不正确)。
一个更好的解决scheme是有一个数据成员,可以在编译时检查。 所以,而不是这个:
.Attributes(style => "width:100%");
具有Style属性的代码可以由编译器检查:
.Attributes.Style = "width:100%";
甚至:
.Attributes.Style.Width.Percent = 100;
对于代码的作者来说,这是更多的工作,但是这种方法利用了C#强大的types检查能力,这有助于防止错误首先进入代码。
事实上,它看起来像Ruby =),至less对我来说,以后的dynamic“查找”的静态资源的使用不适合apidevise的考虑,希望这个聪明的技巧是在该API可选。
我们可以inheritanceIDictionary(或不),并提供一个索引器,当你不需要添加一个键来设置一个值时,它的行为就像一个php数组。 这将是一个有效的.net语义的使用,不仅仅是c#,还需要文档。
希望这可以帮助
恕我直言,这是一个很酷的方式做到这一点。 我们都喜欢命名一个类控制器的事实将使其成为MVC的控制器吗? 所以有些情况下命名很重要。
这里的意图也非常清楚。 这很容易理解.Attribute( book => "something")
会导致book="something"
和.Attribute( log => "something")
会导致log="something"
如果你把它看作一个惯例,我想这不应该是一个问题。 我认为,无论你怎么写出更less的代码,明显地expression意思都是好事。
在我看来,这是滥用lambdas。
至于语法的辉煌,我发现style=>"width:100%"
简单的混淆。 特别是因为=>
而不是=
如果方法(func)的名字是很好的select,那么这是一个很好的方法来避免维护头痛(即:添加一个新的function,但忘了把它添加到函数参数映射列表)。 当然,你需要大量地logging它,你最好自动生成该类中的函数的参数文档。
我觉得这不比“魔术弦”更好。 我对这个匿名types也不是很了解。 它需要一个更好和强types的方法。
- 在razor页面中检查login用户angular色
- debuggingASP.NET MVC时,IIS Express会出现“访问被拒绝”错误
- ASP.NET MVC将ActionLink中的ID传递给控制器
- ASP.NET MVC:通过DataAnnotation进行自定义validation
- ASP.NET MVC中表单值被replace的可能的错误
- Razor视图引擎:expression式树可能不包含dynamic操作
- 为什么是ValidateInput(False)不工作?
- 如何使用getJSON,使用post方法发送数据?
- 将JsonRequestBehavior设置为AllowGet时,可以披露什么“敏感信息”