Pergunta

Já ouvi pessoas afirmam que geradores de códigos e modelos T4 não deve ser utilizado. A lógica por trás disso é que, se você está gerando código com um gerador, em seguida, há uma maneira melhor e mais eficiente para construir o código através de genéricos e de templates.

Enquanto eu ligeiramente concordar com esta afirmação acima, eu realmente não tenho encontrado formas eficazes de modelos de construção que pode dizer, por exemplo instanciar si. Em outras palavras eu nunca pode fazer:

return new T();

Além disso, se eu quero gerar código com base nos valores de banco de dados Eu descobri que usando Microsoft.SqlServer.Management.SMO em conjunto com modelos T4 ter sido maravilhoso para a geração de quantidades maciças de código sem ter que copiar / colar ou uso ReSharper.

Muitos dos problemas que tenho encontrado com os genéricos também é que para minha surpresa, há uma série de desenvolvedores que não entendem-los. Quando eu examinar os genéricos para uma solução, há momentos em que fica complicado porque C # estados que você não pode fazer algo que pode parecer lógico na minha mente.

Quais são seus pensamentos? Você prefere construir um gerador, ou você prefere usar os genéricos? Além disso, o quão longe pode genéricos ir? Eu sei que uma quantidade razoável sobre os genéricos, mas há armadilhas e ciladas que eu sempre executar em que me causa a recorrer a um modelo de T4.

O que é a maneira mais adequada de lidar com situações em que você precisa de uma grande quantidade de flexibilidade? Oh e como um bônus para essa pergunta, quais são bons recursos sobre C # e Genéricos?

Foi útil?

Solução

Você pode fazer nova T (); se você fizer isso

public class Meh<T>
  where T : new()
{
  public static T CreateOne()
  {
    return new T();
  }
}

Como para o código-geradores. Eu uso uma a cada dia sem problemas. Eu estou usando um agora de fato: -)

Os genéricos resolver um problema, código-geradores resolver outro. Por exemplo, criar um modelo de negócio usando um editor de UML e, em seguida, gerar suas aulas com código de persistência como eu faço o tempo todo usando esta ferramenta não poderia ser alcançado com os genéricos, porque cada classe persistente é completamente diferente.

Como para uma boa fonte sobre os genéricos. A melhor tem que ser livro Jon Skeet é claro! : -)

Outras dicas

Como o criador de T4, eu tive que defender essa pergunta algumas vezes como você pode imaginar: -)

A minha convicção é que no seu melhor geração de código é um passo no caminho para a produção de valor equivalente usando bibliotecas reutilizáveis.

Como muitos outros já disseram, o conceito-chave para manter DRY é nunca, nunca alterar o código gerado manualmente, mas sim preservar a sua capacidade de se regenerar quando as alterações de metadados fonte ou você encontrar um bug no gerador de código. Nesse ponto, o código gerado tem muitas das características de código objeto e você não correr em copiar / colar problemas tipo.

Em geral, é muito menos esforço para produzir um gerador de código parametrizado (especialmente com sistemas baseados em modelos) do que para projetar corretamente uma biblioteca de base de alta qualidade que obtém o custo de utilização até o mesmo nível, por isso é uma rápida maneira de obter valor a partir de erros de repetição de consistência e remover.

No entanto, eu ainda acredito que o sistema acabado seria mais frequentemente ser melhorada por ter código menos total. Se nada mais, a sua pegada de memória seria quase sempre significativamente menor (embora as pessoas tendem a pensar de genéricos como custo gratuitos a este respeito, que eles certamente não são).

Se você percebeu algum valor usando um gerador de código, em seguida, isso muitas vezes compra-lhe algum tempo, dinheiro ou boa vontade para investir na colheita de uma biblioteca a partir da base de código gerado. Você pode então incrementalmente reengenharia o gerador de código para atingir a nova biblioteca e esperamos gerar muito menos código. Enxágüe e repita.

Um contraponto interessante que tem sido feito para mim e que surge nesta discussão é que ricos, complexos e bibliotecas paramétricas não são a coisa mais fácil em termos de curva de aprendizagem, especialmente para aqueles que não profundamente imerso na plataforma. Cumprindo com a geração de código para estruturas básicas mais simples pode produzir detalhado código, mas que muitas vezes pode ser bastante simples e fácil de ler.

Claro, onde você tem um monte de variância e parametrização extremamente rica em seu gerador, você pode ser apenas trocando complexidade de um seu produto para a complexidade em seus modelos. Este é um caminho fácil para slide em e pode fazer a manutenção tanto de uma dor de cabeça -. Atente para que

código de geração não é mau e não cheiro! A chave é gerar o código certo na hora certa. Eu acho que T4 é grande - eu só usá-lo ocasionalmente, mas quando eu faço isso é muito útil. Dizer, incondicionalmente, que o código de geração é ruim é incondicionalmente louco!

Parece-me geradores de código são muito bem contanto que a geração de código é parte de seu processo de construção normal, em vez de algo que você executar uma vez e, em seguida, manter a sua saída. Eu adicionar esta ressalva porque se basta usar o gerador de código uma vez e descartar os dados que o criou, você está apenas criando automaticamente uma enorme violação DRY e dor de cabeça de manutenção; enquanto gerando o código de cada vez que efetivamente significa que o que você está usando para fazer o gerador é o código fonte real, e os arquivos gerados são etapas compilar apenas intermediários que você deve em sua maioria ignoram.

Lex e YACC são exemplos clássicos de ferramentas do permitem especificar a funcionalidade de uma maneira eficiente e gerar código eficiente a partir dele. Tentando fazer o seu trabalho à mão irá prolongar o seu tempo de desenvolvimento e, provavelmente, produzir um código menos eficiente e menos legível. E enquanto você poderia algo certamente incorporar como lex e yacc diretamente em seu código e fazer o seu trabalho em tempo de execução em vez de em tempo de compilação, que certamente acrescentam complexidade considerável para o seu código e retardá-lo. Se você realmente precisa mudar sua especificação em tempo de execução pode valer a pena, mas na maioria dos casos normais usando lex / yacc para gerar o código para você em tempo de compilação é uma grande vitória.

A porcentagem boa do que está no Visual Studio 2010 não seria possível sem a geração de código. Entity Framework não seria possível. O simples ato de arrastar e soltar um controle em um formulário não seria possível, nem seria Linq. Para dizer que a geração de código não deve ser usado é estranho que tantos usá-lo sem pensar mesmo sobre ele.

Talvez seja um pouco duro, mas para mim a geração de código cheiros.

Essa geração de código é utilizado meios que existem inúmeros princípios comuns subjacentes que podem ser expressas em uma "Não se repita" moda. Pode demorar um pouco mais, mas é gratificante quando você acabar com as classes que contêm apenas os bits que realmente mudar, com base em uma infra-estrutura que contém a mecânica.

Como a Generics ... não, eu não tenho muitos problemas com ele. A única coisa que atualmente não trabalho está dizendo que

List<Animal> a = new List<Animal>();
List<object> o = a;

Mas mesmo isso será possível na próxima versão do C #.

Mais meios de código mais complexidade. Mais meios de complexidade mais lugares para erros de esconder, o que significa ciclos de correção mais longo, que no meio da volta custos mais elevados ao longo do projeto.

Sempre que possível, prefiro minimizar a quantidade de código para fornecer funcionalidade equivalente; o ideal é usar dinâmico (programática) se aproxima ao invés de geração de código. Reflexo, atributos, aspectos e genéricos fornecem muitas opções para uma estratégia seca, deixando geração como um último recurso.

Geração de código é para mim uma solução para muitos problemas encontrados na linguagem, quadros, etc. Eles não são maus por si mesmos, eu diria que é muito, muito ruim (ou seja, o mal) para liberar uma linguagem (C #) e um quadro que forças que você copiar e colar (swap sobre as propriedades, eventos desencadeantes, a falta de macros) ou usar números mágicos (WPF de ligação).

Então, eu choro, mas eu usá-los, porque eu tenho que.

Eu usei T4 para geração de código e também Genéricos. Ambos são bons, têm seus prós e contras, e são adequados para diferentes fins.

No meu caso, eu uso T4 para gerar Entidades, DAL e BLL com base em um esquema de banco de dados. No entanto, DAL e BLL referência a um mini-ORM I construído, com base em genéricos e reflexão. Então eu acho que você pode usá-los lado a lado, enquanto você manter o controle e mantê-lo pequeno e simples.

T4 gera código estático, enquanto Generics é dinâmico. Se você usar Generics, você usar a reflexão que é dito ser menos eficaz do que a solução "hard-coded". Claro que você pode armazenar em cache os resultados de reflexão.

Quanto a "voltar novo T ();", eu uso Métodos dinâmicos como este:

public class ObjectCreateMethod
    {
    delegate object MethodInvoker();
    MethodInvoker methodHandler = null;

    public ObjectCreateMethod(Type type)
    {
        CreateMethod(type.GetConstructor(Type.EmptyTypes));
    }

    public ObjectCreateMethod(ConstructorInfo target)
    {
        CreateMethod(target);
    }

    void CreateMethod(ConstructorInfo target)
    {
        DynamicMethod dynamic = new DynamicMethod(string.Empty,
                    typeof(object),
                    new Type[0],
                    target.DeclaringType);
        ILGenerator il = dynamic.GetILGenerator();
        il.DeclareLocal(target.DeclaringType);
        il.Emit(OpCodes.Newobj, target);
        il.Emit(OpCodes.Stloc_0);
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Ret);

        methodHandler = (MethodInvoker)dynamic.CreateDelegate(typeof(MethodInvoker));
    }

    public object CreateInstance()
    {
        return methodHandler();
    }
}

Então, eu chamo-lhe assim:

ObjectCreateMethod _MetodoDinamico = new ObjectCreateMethod(info.PropertyType);
object _nuevaEntidad = _MetodoDinamico.CreateInstance();

Os genéricos e geração de código são duas coisas diferentes. Em alguns casos, você pode usar os genéricos em vez de geração de código e para aqueles que acreditam que deveria. Para a outra geração de código casos é uma ferramenta poderosa.

Para todos os casos em que você simplesmente precisa para gerar código baseado em alguma entrada de dados, geração de código é o caminho a percorrer. A mais óbvia, mas nem por isso o único exemplo é o editor de formulários em Visual Studio. Aqui, a entrada é o de dados designer e a saída é o código. Neste caso, os genéricos é realmente não ajuda em nada, mas é muito bom que VS simplesmente gera o código com base no layout GUI.

Geradores de código poderia ser considerado um cheiro de código que indicam uma falha ou falta de funcionalidade no langauge alvo.

Por exemplo, embora tenha sido dito aqui que "Objetos que persistem não podem ser generalizados", seria melhor pensar nisso como "Objetos em C # que persistem automaticamente seus dados não podem ser generalizados em C #", porque Eu certamente pode em Python com o uso de vários métodos.

A abordagem Python poderia, no entanto, ser emulado em linguagens estáticas através do uso de operador [] (method_name como string), que quer retorna um functor ou uma string, dependendo dos requisitos. Infelizmente essa solução não é sempre o caso, e retornando um functor pode ser inconveniente.

O ponto que eu estou fazendo é que os geradores de código indicam uma falha em um idioma escolhido que são abordados, fornecendo uma mais conveniente especializada sintaxe para o problema específico em questão.

A copiar / colar tipo de código gerado (como ORMs fazer) também pode ser muito útil ...

Você pode criar seu banco de dados, e depois ter o ORM gerar uma cópia do que a definição de banco de dados expressa na sua língua favorita.

A vantagem vem quando você mudar a sua definição original (banco de dados), prima de compilação e o ORM (se você tiver uma boa) pode re-gera a sua cópia da definição. Agora todas as referências a seu banco de dados pode ser verificada pelo verificador de tipos compiladores e seu código não será compilado quando você estiver usando tabelas ou colunas que não existem mais.

Pense sobre isso: Se eu chamar um método algumas vezes no meu código, que não estou referindo-se ao nome que dei a este método originalmente? Eu continuo repetindo esse nome mais e mais ... projetistas da linguagem reconheceu este problema e veio com "Tipo-segurança" como a solução. Não remover as cópias (como DRY sugere que devemos fazer), mas verificá-los para correção em seu lugar.

O código ORM gerado traz a mesma solução quando se refere a nomes de tabelas e colunas. Não remover as cópias / referências, mas trazendo a definição de banco de dados no seu idioma (tipo seguro), onde você pode se referir a classes e propriedades em vez. Juntamente com o tipo compiladores verificação, isto resolve um problema semelhante de forma semelhante: erros de Garantia de compilação-tempo em vez de os de tempo de execução quando você se referir a tabelas ultrapassadas ou erros ortográficos (classes) ou colunas (propriedades)

.

Citação: Eu realmente não tenho encontrado formas eficazes de modelos de construção que pode dizer, por exemplo instanciar si. Em outras palavras eu nunca pode fazer:

retornar nova T ();

public abstract class MehBase<TSelf, TParam1, TParam2>
    where TSelf : MehBase<TSelf, TParam1, TParam2>, new()
{
    public static TSelf CreateOne()
    {
        return new TSelf();
    }
}

public class Meh<TParam1, TParam2> : MehBase<Meh<TParam1, TParam2>, TParam1, TParam2>
{
    public void Proof()
    {
        Meh<TParam1, TParam2> instanceOfSelf1 = Meh<TParam1, TParam2>.CreateOne();
        Meh<int, string> instanceOfSelf2 = Meh<int, string>.CreateOne();
    }
} 

geração de código, como os genéricos, modelos e outros tais atalhos, é uma ferramenta poderosa. E como a maioria das ferramentas poderosas, que amplifica o capaility do seu utilizador para o bem e para o mal -. Eles não podem ser separados

Então, se você entender o seu gerador de código completamente, antecipar tudo que irá produzir, e porquê, e pretende que ele a fazê-lo por razões válidas, então têm-no. Mas não usá-lo (ou qualquer um dos outra técnica) para começar após um lugar onde você não é a certeza de onde você está indo, ou como chegar lá.

Algumas pessoas pensam que, se você começar o seu problema atual resolvidos e algum comportamento implementado, você é ouro. Nem sempre é óbvio o quanto cruft e opacidade você deixar em sua trilha para a próxima desenvolvedor (que pode ser você mesmo.)

Por que ser capaz de copiar / colar muito, muito rápido, torná-lo mais aceitável?

Essa é a única justificação para a geração de código que eu posso ver.

Mesmo que o gerador fornece toda a flexibilidade que você precisa, você ainda tem que aprender a usar essa flexibilidade -. Que é ainda outra camada de aprendizado e testes necessários

E mesmo se ele é executado em tempo zero, ainda incha o código.

Eu rolei minha própria classe de acesso de dados. Ele sabe tudo sobre conexões, transações armazenados parms procedimento, etc, etc, e eu só tinha de escrever todas as coisas o ADO.NET uma vez.

Agora, é tanto tempo desde que eu tinha para escrever (ou olhar mesmo em) qualquer coisa com um objeto de conexão nele, que eu seria duramente pressionado para lembrar a sintaxe de improviso.

scroll top