C # está se tornando mais difícil de ler?

15

À medida que o C # progrediu, muitos recursos de idioma foram adicionados. Chegou ao ponto em que está se tornando ilegível para mim.

Como exemplo, considere o seguinte recorte de código do código Caliburn.Micro aqui :

            container = CompositionHost.Initialize(
                   new AggregateCatalog(
                      AssemblySource.Instance.
                             Select(x => new AssemblyCatalog(x))
                               .OfType<ComposablePartCatalog>()
                )
            );

Agora, este é apenas um pequeno exemplo.

Eu tenho várias perguntas:

  • Esse é um problema comum ou conhecido?
  • A comunidade C # encontra o mesmo?
  • Esse é um problema com o idioma ou é o estilo usado pelo desenvolvedor?

Existem soluções simples para entender melhor o código de outras pessoas e evitar a criação de código dessa maneira?

Steven Evers
fonte
6
É a função lambda, eles são difíceis de ler até que você realmente os obtenha.
Ryathal
9
Eu acho que você realmente não conhecia a sintaxe tão bem quanto pensava. Com a prática de ler seu exemplo é simples.
ChaosPandion
6
@ Thomas Owens: Puta merda, pelo menos nos dê um tempo para editar perguntas para mantê-las abertas. 15 minutos? Venha agora.
Steven Evers
4
@DannyVarod: 1. "Não" não é uma resposta aceitável 2. A pergunta está encerrada 3. Se você concorda que a pergunta não deveria estar aqui, sinalize / vote para fechá-la ou editá-la para melhorar .
Steven Evers
2
@ Thorbjørn Ravn Andersen - O que se pode aprender com um tapa? Isso não contém informações!

Respostas:

12

Nota rápida sobre a localização da linguagem deve esclarecer: C # é uma linguagem de programação de uso geral ; ao contrário de C (como C ++), ele busca alta abstração; ao contrário dos dialetos Lisp, ele busca expressividade prática e, ao contrário do Java, é mais agressivo - a Microsoft responde rapidamente à demanda.

É por isso que está se transformando em uma mistura de LINQ, lambdas e novas palavras-chave estranhas - está se adaptando rapidamente a novos domínios de problemas, e essa é realmente uma inclinação escorregadia em direção a uma linguagem tão complexa que poucos conseguem usá-la corretamente (como C ++). Não é um problema com o C # em si, é um problema com qualquer linguagem com esses objetivos ambiciosos.

A comunidade está ciente disso e, mais importante, os caras por trás do C # estão bem cientes disso (as poucas entradas de blog e podcasts sobre o que estava por trás das novas adições no C # 5.0 mostram o quão ruim eles querem manter as coisas simples). A Microsoft está tentando tirar parte da carga de seu carro-chefe para que não se torne um tarpit: a introdução do DLR, um holofote brilhante sobre novos idiomas (F #).

Além disso, o material do curso em C # (incluindo as certificações MS) recomenda estilos de uso diferentes (mas consistentes) para o C #, dependendo do problema - escolha sua arma e siga-a: LINQ de estilo fluente em alguns problemas, lambdas com TPL em outros, simples -old para a maioria.

vski
fonte
2
Você pode explicar o que você quer dizer com "diferentemente dos dialetos Lisp, ele visa à expressividade prática"?
Giorgio
27

Não. O C # está nos dando mais opções para escrever código de forma mais sucinta. Isso pode ser usado ou abusado. Se usado corretamente, facilita a leitura do código. Se usado incorretamente, pode dificultar a leitura do código. Mas programadores ruins sempre tiveram a habilidade de escrever códigos difíceis de ler.

Vamos dar dois exemplos, ambos baseados em exemplos encontrados no StackOverflow sobre o mesmo problema, localizando tipos com um atributo específico:

C # 2.0:

static IEnumerable<Type> GetTypesWithAttribute<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{
    foreach(Assembly assembly in AppDomain.Current.GetAssemblies())
    {
        foreach(Type type in assembly.GetTypes()) 
        {
            if (type.IsDefined(typeof(TAttribute),inherit))
                yield return type;
        }
    }
}

C # 4.0:

IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{ 
    return from assembly in AppDomain.CurrentDomain.GetAssemblies()
           from type in assembly.GetTypes()
           where type.IsDefined(typeof(TAttribute),inherit)
           select type;
}

IMHO, a nova sintaxe facilita muito a leitura.

Pete
fonte
a nova sintaxe é realmente mais fácil de ler, mas mais fácil de entender, como no que está acontecendo sob o capô? O primeiro exemplo é compreensível, o segundo é mais legível.
Nawfal #
4
@nawfal Na minha experiência, as pessoas têm muito mais problemas para entender as construções de "retorno de rendimento" do que as declarações linq; então eu diria que o segundo é mais legível e compreensível. Nem realmente diz o que está acontecendo sob o capô, já que ambos são mapeados para abstrações distantes de como os processadores realmente funcionam.
Chuu 12/03/2013
Para ser sincero, não gosto do LINQ porque ele abstrai os loops e incentiva os codificadores a recuperar dados em consultas LINQ separadas (com loops separados em segundo plano) em vez de um loop com um núcleo mais complicado.
mg30rg
8

Quando o LINQ foi adicionado, ele popularizou um estilo de codificação envolvendo muitos encadeamentos de métodos no estilo Fluente e a passagem de lambdas como parâmetros.

Esse estilo é muito poderoso quando você estiver confortável com ele. No entanto , pode ser abusado para criar código bastante ilegível. Não acho que o seu exemplo seja muito ruim, embora a indentação seja aleatória (e esse é um recurso comum desse estilo de código, o Visual Studio não o indenta automaticamente de maneira consistente).

No meu local de trabalho, discutimos esse estilo de codificação ao revisar nossos padrões de codificação no início deste ano, e decidimos incentivar os desenvolvedores a dividir códigos assim: especificamente, para não criar e inicializar objetos anônimos em uma chamada de função. Portanto, seu código se tornaria:

var assemblyCatalog = AssemblySource.Instance
    .Select(x => new AssemblyCatalog(x))
    .OfType<ComposablePartCatalog>();
var aggregateCatalog = new AggregateCatalog(assemblyCatalog);
container = CompositionHost.Initialize(aggregateCatalog);
Carson63000
fonte
1
Não é meio estranho criar um novo tipo e depois ativá-lo dinamicamente logo depois?
DeadMG 5/12/12
Provavelmente, acabei de detalhar o trecho de código sem nenhuma consideração real sobre qual era sua intenção. Só queria dividir todas as instanciações em suas próprias linhas de código para mostrar que efeito isso teria na aparência do código.
Carson63000
Eu assumo a culpa pelo recuo :(). Aqui está o original: devlicio.us/blogs/rob_eisenberg/archive/2010/07/08/…
1
Concordo plenamente com essa resposta. O problema no trecho de código de exemplo não é a nova sintaxe do C #, é que o escritor tentou ser inteligente com o código "one-liner". Existe uma regra antiga que remonta ao UNIX: tudo deve fazer apenas uma coisa e fazê-lo bem. Aplica-se à classe, aplica-se ao método e, claro, aplica-se certamente às linhas de código.
Laurent Bourgault-Roy
4

O código no seu exemplo não é facilmente legível porque

  • Ele combina muitas noções (Composição, Catálogos, Agregados, Assemblies, ComposableParts ...) com vários níveis de aninhamento, enquanto o código de chamada normalmente deve lidar com apenas um nível. Parece um pouco com uma violação da Lei de Demeter apenas com chamadas de método aninhadas em vez de uma cadeia de sub-subpropriedades. Isso atrapalha um pouco a intenção por trás da linha de código.

  • Há um uso estranho de singleton - AssemblySource.Instance.Select()implica que Instance é um IEnumerable, o que é um pouco estranho.

  • A variável x na expressão lambda é feia. Geralmente, você tenta dar às suas variáveis ​​lambda a intenção de revelar nomes - ajuda o leitor a identificar de que tipo de dados se trata a função, mesmo que ele não esteja familiarizado com as lambdas.

  • Não está claro por que você deve filtrar com OfType<T>()uma coleção de objetos que acabou de atualizar ...

No entanto, todas essas falhas estão relacionadas à maneira como o desenvolvedor original escreveu o código e a quão expressivo ele o projetou. Isso não é algo específico para as versões mais recentes do C # e a aparência das expressões lambda.

De uma maneira mais geral, ajuda se o leitor do seu código sabe como funcionam as lambdas, mas com uma nomeação clara o suficiente, você quase sempre consegue tornar seu código legível, mesmo para alguém com um background anterior ao .NET 3.5.

guillaume31
fonte
2

Sempre que uma nova versão da linguagem popular ganha novas construções, surgem debates semelhantes.

Eu também tive essas dúvidas anos atrás (http://gsscoder.blogspot.it/2009/08/use-csharps-features-wisely.html).

Na minha opinião, o C # está evoluindo de uma maneira elegante e consistente.

O código no seu exemplo não é complexo, talvez você não esteja sendo usado com expressões lamda.

O problema é que o C # não é apenas uma linguagem orientada a objetos, agora também suporta construções funcionais (http://archive.msdn.microsoft.com/FunctionalCSharp/).

gsscoder
fonte
1

Demorei um pouco para entender a sintaxe do Lambda, e eu sou do fundo da matemática e da física. É um pouco semelhante às expressões matemáticas, mas elas usam -> em vez de =>:

Exemplo de matemática f (x): = x-> x + 2 é lido como "A função f de x é definida como x mapeia para x + 2

c # exemplo del myDelegate = x => x +2; isto lê como "myDelegate é uma função tal que myDelegate (x) mapeia x para x + 2

A inconsistência entre matemática e programação realmente não ajuda, embora eu suponha -> já havia sido adotado em C ++ como "propriedade de" (urrgghh!)

http://en.wikipedia.org/wiki/List_of_mathematical_symbols

Andy R
fonte
"embora eu suponha -> já havia sido considerado em C ++ como" propriedade de "(urrgghh!)" Como se! Mal em C ++ significa "propriedade do alocado dinamicamente ...". Se algo não é dinâmico, você usa o período como em qualquer outro idioma.
mg30rg