É bem sabido para aqueles familiarizados com a história que o C # e o .NET framework começaram como "Delphi reescrito para se parecer com Java", arquitetado pelo desenvolvedor principal por trás do Delphi, Anders Hejlsberg. As coisas divergiram bastante desde então, mas no início as semelhanças eram tão óbvias que havia até algumas especulações sérias de que o .NET era realmente o produto da Borland.
Ultimamente, tenho observado algumas coisas sobre .NET, e um dos recursos mais interessantes e úteis do Delphi parece estar totalmente ausente: o conceito de classe como um tipo de dados de primeira classe. Para quem não conhece, o tipo TClass
representa uma referência a uma classe, semelhante ao Type
tipo no .NET. Mas onde o .NET usa Type
para reflexão, o Delphi usa TClass
como uma parte interna muito importante da linguagem. Ele permite vários idiomas úteis que simplesmente não existem e não podem existir sem ele, como variáveis de subtipo de classe e métodos de classe virtual.
Toda linguagem OO possui métodos virtuais, nos quais diferentes classes implementam o mesmo conceito fundamental de um método de maneiras diferentes e, em seguida, o método correto é chamado em tempo de execução com base no tipo real da instância do objeto em que é chamado. O Delphi estende esse conceito para classes: se você tiver uma referência TClass definida como um subtipo de classe específico (ou seja, class of TMyClass
significa que a variável pode aceitar qualquer referência de classe que herda TMyClass
, mas não algo fora da hierarquia) que possui métodos virtuais de escopo de classe anexados a eles podem ser chamados sem uma instância usando o tipo real da classe. A aplicação desse padrão aos construtores torna uma implementação de fábrica trivial, por exemplo.
Parece não haver nada equivalente no .NET. Por mais úteis que sejam as referências de classe (e principalmente os construtores virtuais e outros métodos de classe virtual!), Alguém disse alguma coisa sobre por que foram deixados de fora?
Exemplos Específicos
Desserialização de formulários
O Delphi VCL salva formulários em DFM
formato, um DSL para descrever uma hierarquia de componentes. Quando o leitor de formulários analisa dados do DFM, ele é executado nos objetos descritos desta maneira:
object Name: ClassName
property = value
property = value
...
object SubObjectName: ClassName
...
end
end
O interessante aqui é a ClassName
parte. Cada classe de componente registra o seu TClass
com o sistema de streaming de componente no initialization
momento (pense que os construtores estáticos, apenas um pouco diferentes, garantem que aconteça imediatamente na inicialização.) Isso registra cada classe em um mapa de hash de string-> TClass com o nome da classe como chave.
Cada componente é descendente TComponent
, que possui um construtor virtual que usa um único argumento Owner: TComponent
,. Qualquer componente pode substituir esse construtor para fornecer sua própria inicialização. Quando o leitor DFM lê um nome de classe, ele pesquisa o nome no hashmap mencionado acima e recupera a referência de classe correspondente (ou gera uma exceção se não estiver lá) e chama o construtor TComponent virtual, conhecido por ser bom porque a função de registro usa uma referência de classe necessária para descender do TComponent e você acaba com um objeto do tipo apropriado.
Na falta disso, o equivalente do WinForms é ... bem ... uma grande bagunça para ser franco, exigindo que qualquer nova linguagem .NET reimplemente completamente sua própria forma (des) serialização. Isso é um pouco chocante quando você pensa sobre isso; como o objetivo de ter um CLR é permitir que vários idiomas usem a mesma infraestrutura básica, um sistema no estilo DFM faria todo sentido.
Extensibilidade
Uma classe de gerenciador de imagens que escrevi pode ser fornecida com uma fonte de dados (como um caminho para seus arquivos de imagem) e carregar novos objetos de imagem automaticamente se você tentar recuperar um nome que não esteja na coleção, mas esteja disponível na fonte de dados. Possui uma variável de classe digitada como class of
a classe de imagem base, representando a classe de quaisquer novos objetos a serem criados. Ele vem com um padrão, mas há alguns pontos, ao criar novas imagens com finalidades especiais, que as imagens devem ser configuradas de maneiras diferentes. (Criando-o sem um canal alfa, recuperando metadados especiais de um arquivo PNG para especificar o tamanho do sprite etc.)
Isso pode ser feito escrevendo quantidades extensas de código de configuração e passando opções especiais para todos os métodos que podem acabar criando um novo objeto ... ou você pode simplesmente criar uma subclasse da classe de imagem base que substitui um método virtual em que o aspecto em questão é configurado e, em seguida, use um bloco try / finalmente para substituir temporariamente a propriedade "classe padrão", conforme necessário, e depois restaurá-la. Fazer isso com variáveis de referência de classe é muito mais simples, e não é algo que poderia ser feito com genéricos.
TClass
é útil, com algum código de exemplo? Na minha pesquisa superficial na Internet TClass
, estou descobrindo que isso TClass
pode ser passado como um parâmetro. Isso é feito no .NET usando Genéricos. Os métodos de fábrica são simplesmente marcados static
no .NET e não requerem uma instância de classe para serem executados.
TClass
é um recurso de linguagem fundamental que requer suporte do compilador. Você não pode "escrever o seu próprio" sem escrever seu próprio idioma e, para o .NET, isso não seria suficiente porque o modelo de objeto é definido pelo CLR, não por idiomas individuais. É algo que literalmente precisa fazer parte da própria estrutura .NET, ou não pode existir.