Estou olhando para o componente MvcContrib Grid e estou fascinado, mas ao mesmo tempo repelido, por um truque sintático usado na sintaxe Grid :
.Attributes(style => "width:100%")
A sintaxe acima define o atributo de estilo do HTML gerado para width:100%
. Agora, se você prestar atenção, 'estilo' não é especificado em nenhum lugar. É deduzido do nome do parâmetro na expressão! Eu tive que cavar isso e descobri onde a 'mágica' acontece:
Hash(params Func<object, TValue>[] hash)
{
foreach (var func in hash)
{
Add(func.Method.GetParameters()[0].Name, func(null));
}
}
Portanto, de fato, o código está usando o nome formal dos parâmetros em tempo de compilação para criar o dicionário de pares nome-valor do atributo. A construção da sintaxe resultante é realmente muito expressiva, mas ao mesmo tempo muito perigosa.
O uso geral de expressões lambda permite a substituição dos nomes usados sem efeito colateral. Vejo um exemplo em um livro que diz collection.ForEach(book => Fire.Burn(book))
que sei que posso escrever no meu código collection.ForEach(log => Fire.Burn(log))
e que significa a mesma coisa . Mas com a sintaxe MvcContrib Grid aqui, de repente, encontro código que olha ativamente e toma decisões com base nos nomes que escolho para minhas variáveis!
Então, essa prática comum é com a comunidade C # 3.5 / 4.0 e os amantes de expressões lambda? Ou é um especialista em truques desonestos com o qual não devo me preocupar?
fonte
Respostas:
Isso tem interoperabilidade ruim. Por exemplo, considere este exemplo de C # - F #
C #:
F #:
Resultado:
"arg" é impresso (não "yadda").
Como resultado, os projetistas de bibliotecas devem evitar esses tipos de 'abusos' ou, pelo menos, fornecer uma sobrecarga 'padrão' (por exemplo, que leva o nome da string como um parâmetro extra) se quiserem ter uma boa interoperabilidade nas linguagens .Net.
fonte
Acho isso estranho, não tanto por causa do nome , mas porque o lambda é desnecessário ; poderia usar um tipo anônimo e ser mais flexível:
Esse é um padrão usado em grande parte do ASP.NET MVC (por exemplo) e tem outros usos (uma advertência , observe também os pensamentos de Ayende se o nome for um valor mágico em vez de específico para o chamador)
fonte
HtmlAttributes
classe com os atributos esperados (para intellisense), e simplesmente ignorar aqueles comnull
valores ...Só queria jogar na minha opinião (eu sou o autor do componente de grade MvcContrib).
Definitivamente, isso é abuso de linguagem - não há dúvida sobre isso. No entanto, eu realmente não consideraria isso contra-intuitivo - quando você olha para uma ligação
Attributes(style => "width:100%", @class => "foo")
, acho bastante óbvio o que está acontecendo (certamente não é pior do que a abordagem de tipo anônimo). Do ponto de vista intelectual, eu concordo que é bastante opaco.
Para os interessados, algumas informações básicas sobre seu uso no MvcContrib ...
Adicionei isso à grade como uma preferência pessoal - não gosto do uso de tipos anônimos como dicionários (ter um parâmetro que aceite "objeto" seja tão opaco quanto um que use os parâmetros Func []) e o inicializador de coleção de Dicionário seja bastante detalhado (eu também não sou fã de interfaces fluentes verbais, por exemplo, ter que encadear várias chamadas para um Atributo ("estilo", "exibição: nenhuma"). Atributo ("classe", "foo") etc.)
Se o C # tivesse uma sintaxe menos detalhada para literais do dicionário, não teria me incomodado em incluir essa sintaxe no componente de grade :)
Também quero ressaltar que o uso disso no MvcContrib é completamente opcional - esses são métodos de extensão que envolvem sobrecargas que usam um IDictionary. Eu acho importante que, se você fornecer um método como esse, também ofereça suporte a uma abordagem mais "normal", por exemplo, para interoperabilidade com outros idiomas.
Além disso, alguém mencionou a 'sobrecarga de reflexão' e eu só queria ressaltar que realmente não há muita sobrecarga nessa abordagem - não há reflexão em tempo de execução ou compilação de expressão envolvida (consulte http://blog.bittercoder.com /PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx ).
fonte
eu preferiria
É muito mais explícito e padrão e nada está sendo ganho com o uso de lambdas.
fonte
html.Attributes.Add("style", "width:100%");
não lê tão bem quantostyle = "width:100%"
(o html real gerado), enquantostyle => "width:100%"
está muito próximo da aparência no HTML resultante.Bem-vindo ao Rails Land :)
Não há realmente nada de errado com isso, desde que você saiba o que está acontecendo. (É quando esse tipo de coisa não está bem documentado que existe um problema).
A totalidade da estrutura do Rails é construída sobre a idéia de convenção sobre configuração. Nomear as coisas de uma certa maneira leva você a uma convenção que eles estão usando e você obtém muitas funcionalidades gratuitamente. Seguir a convenção de nomenclatura leva você aonde você está indo mais rápido. A coisa toda funciona de maneira brilhante.
Outro lugar onde eu já vi um truque como esse é nas asserções de chamada de método no Moq. Você passa em um lambda, mas o lambda nunca é executado. Eles apenas usam a expressão para se certificar de que a chamada do método ocorreu e, se não, gera uma exceção.
fonte
Isso é horrível em mais de um nível. E não, isso não é nada como Ruby. É um abuso de C # e .NET.
Houve muitas sugestões de como fazer isso de maneira mais direta: tuplas, tipos anônimos, uma interface fluente e assim por diante.
O que o torna tão ruim é que é apenas uma maneira de gostar do seu próprio bem:
O que acontece quando você precisa chamar isso do Visual Basic?
.Attributes(Function(style) "width:100%")
É completamente contra-intuitivo, e o intellisense fornecerá pouca ajuda para descobrir como distribuir coisas.
É desnecessariamente ineficiente.
Ninguém terá idéia de como mantê-lo.
Qual é o tipo de argumento usado nos atributos? é isso
Func<object,string>
? Como essa intenção é reveladora? O que sua documentação do intellisense vai dizer: "Por favor, desconsidere todos os valores do objeto"?Eu acho que você está completamente justificado por ter esses sentimentos de repulsa.
fonte
Eu estou no campo "brilho da sintaxe", se eles documentarem isso claramente, e parece legal demais, quase não há problema com isso imo!
fonte
Ambos. É abuso de expressões lambda E brilho de sintaxe.
fonte
Quase nunca me deparei com esse tipo de uso. Eu acho que é "inadequado" :)
Esta não é uma maneira comum de uso, é inconsistente com as convenções gerais. Esse tipo de sintaxe possui prós e contras, é claro:
Contras
Prós
Bottom line - no design da API pública, eu teria escolhido uma maneira mais explícita.
fonte
Não, certamente não é uma prática comum. É contra-intuitivo, não há como apenas olhar o código para descobrir o que ele faz. Você precisa saber como é usado para entender como é usado.
Em vez de fornecer atributos usando uma matriz de delegados, os métodos de encadeamento seriam mais claros e apresentariam melhor desempenho:
Embora isso seja um pouco mais para digitar, é claro e intuitivo.
fonte
.Attribute("style", "width:100%")
me dástyle="width:100%"
, mas pelo que sei, isso pode me darfoooooo
. Não estou vendo a diferença.Posso usar isso para cunhar uma frase?
lambda mágica (n): uma função lambda usada apenas com o objetivo de substituir uma sequência mágica.
fonte
O que há de errado com o seguinte:
fonte
Todo esse discurso retórico sobre "horror" é um monte de caras c # de longa data exagerando (e eu sou um programador de C # de longa data e ainda sou um grande fã da linguagem). Não há nada de horrível nessa sintaxe. É apenas uma tentativa de fazer a sintaxe parecer mais com o que você está tentando expressar. Quanto menos "ruído" houver na sintaxe para algo, mais fácil o programador poderá entender. Diminuir o ruído em uma linha de código ajuda apenas um pouco, mas deixe que isso se acumule em cada vez mais códigos, e isso acaba sendo um benefício substancial.
Esta é uma tentativa do autor de buscar os mesmos benefícios que as DSLs oferecem a você - quando o código simplesmente "se parece" com o que você está tentando dizer, você alcançou um lugar mágico. Você pode discutir se isso é bom para interoperabilidade ou se é suficiente mais agradável que métodos anônimos para justificar parte do custo da "complexidade". É justo o suficiente ... portanto, em seu projeto, você deve fazer a escolha certa sobre usar esse tipo de sintaxe. Mas ainda assim ... esta é uma tentativa inteligente de um programador de fazer o que, no final do dia, todos estamos tentando fazer (se percebemos ou não). E o que estamos tentando fazer é o seguinte: "Diga ao computador o que queremos que ele faça em uma linguagem o mais próxima possível de como pensamos sobre o que ele quer que seja".
Aproximar-se de expressar nossas instruções para computadores da mesma maneira que pensamos internamente é a chave para tornar o software mais sustentável e mais preciso.
EDIT: Eu disse "a chave para tornar o software mais sustentável e mais preciso", que é um pouco exagerado de unicórnio. Eu mudei para "uma chave".
fonte
Esse é um dos benefícios das árvores de expressão - é possível examinar o próprio código para obter informações adicionais. É assim que
.Where(e => e.Name == "Jamie")
pode ser convertido na cláusula SQL Where equivalente. Este é um uso inteligente de árvores de expressão, embora eu esperasse que não fosse além disso. Qualquer coisa mais complexa provavelmente será mais difícil do que o código que ela espera substituir, então eu suspeito que seja auto-limitante.fonte
É uma abordagem interessante. Se você restringisse o lado direito da expressão apenas a constantes, poderia implementar usando
O que eu acho que é o que você realmente deseja, em vez do delegado (você está usando o lambda para obter nomes de ambos os lados). Veja a implementação ingênua abaixo:
Isso pode até resolver a preocupação de interoperabilidade entre idiomas mencionada anteriormente no encadeamento.
fonte
O código é muito inteligente, mas potencialmente causa mais problemas que resolve.
Como você apontou, agora existe uma dependência obscura entre o nome do parâmetro (estilo) e um atributo HTML. Nenhuma verificação do tempo de compilação é feita. Se o nome do parâmetro estiver digitado incorretamente, a página provavelmente não terá uma mensagem de erro em tempo de execução, mas será muito mais difícil encontrar um erro lógico (sem erros, mas com comportamento incorreto).
Uma solução melhor seria ter um membro de dados que possa ser verificado em tempo de compilação. Então, em vez disso:
O código com uma propriedade Style pode ser verificado pelo compilador:
ou até:
Isso é mais trabalhoso para os autores do código, mas essa abordagem tira proveito da forte capacidade de verificação de tipo do C #, que ajuda a impedir que erros entrem no código em primeiro lugar.
fonte
de fato, parece Ruby =), pelo menos para mim o uso de um recurso estático para uma "pesquisa" dinâmica posterior não se encaixa nas considerações de design da API, espero que esse truque inteligente seja opcional nessa API.
Poderíamos herdar do IDictionary (ou não) e fornecer um indexador que se comporte como uma matriz php quando você não precisar adicionar uma chave para definir um valor. Será um uso válido da semântica .net e não apenas c #, e ainda precisará de documentação.
espero que isto ajude
fonte
Na minha opinião, é abuso das lambdas.
Quanto ao brilho da sintaxe, acho
style=>"width:100%"
bastante confuso. Particularmente por causa do em=>
vez de=
fonte
IMHO, é uma maneira legal de fazê-lo. Todos nós gostamos do fato de nomear um controlador de classe para torná-lo um controlador no MVC, certo? Portanto, há casos em que a nomeação é importante.
Também a intenção é muito clara aqui. É muito fácil entender que
.Attribute( book => "something")
resultarábook="something"
e.Attribute( log => "something")
resultará emlog="something"
Eu acho que não deve ser um problema se você o tratar como uma convenção. Sou da opinião de que tudo o que faz com que você escreva menos código e torne a intenção óbvia é uma coisa boa.
fonte
Se os nomes dos métodos (func) forem bem escolhidos, essa é uma maneira brilhante de evitar dores de cabeça na manutenção (por exemplo: adicione uma nova função, mas esqueça de adicioná-la à lista de mapeamento de parâmetros de função). Obviamente, é necessário documentá-lo muito e é melhor gerar automaticamente a documentação para os parâmetros, a partir da documentação para as funções nessa classe ...
fonte
Eu acho que isso não é melhor do que "cordas mágicas". Também não sou muito fã dos tipos anônimos. Precisa de uma abordagem melhor e fortemente tipada.
fonte