Skip to content

Sumários

fmcarvalho edited this page Jun 6, 2019 · 34 revisions

Aulas:

Livro de Referência: Essential .NET, Volume 1: The Common Language Runtime,


18-02-2019

  • Regras de Avaliação
  • Bibliografia recomendada
  • Ferramentas: csc, ildasm, ilasm, peverify
  • Página no Github

  • Introdução ao âmbito da disciplina de Ambientes Virtuais de Execução--AVE
  • AVE ~ VM (virtual machine)
  • Foco no AVE e não na linguagem C#
  • Evolução histórica das plataformas .Net e Java
  • Exemplos de linguagens para a JVM (Java, kotlin, Scala) e linguagens para .NET (C#, F#, VB)
  • Essencial do C# semelhante à linguagem Java. E.g. class A { static void foo() { }}
  • Utilização do compilador de C# de linha de comando (csc)
  • 2 tipos de módulos em .NET: .exe (por omissão) e .dll (csc /t:library)
  • Configuração do ambiente consola VsDevCmd.bat

21-02-2019

  • Distinção entre especificação de um AVE (e.g. JVM, CLI) e sua implementação (e.g. Java SE HotSpot, .Net Framework, .Net Core)
  • Vantagens do Desenvolvimento de Sw por Componentes:
    • Redução da complexidade;
    • Promover a reutilização.
  • Noção de Componente como uma Peça de Software Reutilizável
  • Distinção entre os papéis do programador que fornece um componente e o programador que reutiliza esse componente para a produção de uma aplicação, ou outro componente.
  • Unidade de distribuição de componentes em Java: .class e Jar (conjunto de .class).
  • Ambiente Unmanaged <versus> Managed

  • Cenário unmanaged e componentes com código em linguagem máquina (ficheiros .obj);
  • Dificuldades da reutilização de componentes em ambiente unmanaged:
    • componentes em duas partes: header + obj
    • necessitam de um ficheiro de header que descreve os tipos definidos no componente
    • necessidade de build para diferentes arquitecturas
    • mudanças estruturais (modificação do ficheiro de header) obrigam à recompilação e ligação das aplicações cliente.
    • mudanças de comportamento dos métodos obrigam apenas à ligação das aplicações cliente.
  • Demonstração de um componente que define um tipo Ponto e uma aplicação PontoApp que reutiliza o componente anterior.
  • Ferramentas de compilação e ligação em C:
    • cl /c /Zi -- compila, sem ligar e gera um obj;
    • link /DEBUG comp1.obj comp2.lib etc
    • devenv PontoApp.exe -- abre o Visual Studio e permite fazer debug.

21-02-2019

  • Distinção entre Ligação Estática vs Ligação Dinâmica e Tempo de Compilação vs Tempo de Execução
  • Cenário managed e componentes com código em linguagem intermédia, e.g. bytecodes (Java) ou IL (.net).:
    • Componentes auto-suficentes: IL + metadata (faz o papel do .h)
  • Papel da Metadata na compilação e em tempo de execução---no Verifier do AVE--- garante "safety";
  • Jitter (just-in-time compiler) -- responsável pela tradução do código intermédio (IL ou bytecodes) para linguagem máquina.
  • Verifier -- responsável por garantir a propriedade de safety, e.g. não existem acessos inválidos a memória.
  • Ferramenta peverify
  • Utilização da ferramenta javap (java) e ildasm (.net)

  • Detalhe da Linguagem IL;
  • Modelo de execução em Stack managed;
  • Tabela de argumentos e variáveis locais.
  • Operadores st... e ld...;
  • call, callvirt e newobj

25-02-2019

  • Resolução do TPC
  • Categorias de Tipos: tipos valor e tipos referência;
  • Valores vs Objectos;
  • Tipo Type como representante de um tipo -- Tipo Class representante de um tipo em Java.
  • método GetType() de Object.
  • O resultado do GetType() para o mesmo tipo, retorna sempre a referência para o mesmo representante desse tipo -- a mesma instância de Type.
  • A VM mantém apenas um representante (instância de Type) para cada tipo carregado.
  • Consulta da propriedade BaseType
  • Tipos valor estendem de System.ValueType
  • Tipos valor não podem ser derivados.
  • Teste de igualdade de tipos com base em GetType.
  • GetType() e comparação de identidade com typeof
  • GetType() vs typeof
  • Propriedade .class do Java -- operador typeof(…) do C#
  • Resolução do TPC02
  • Método GetInterfaces() de Type.
  • Reflexão -- System.Reflection (.Net) <=> java.lang.reflect (Java)
  • Reflexão -- API object oriented sobre a metadata
  • Reflexão -- segue as regras que caracterizam um sistema de tipos (CTS em .net)
  • API de Reflexão do .net (System.Reflection Namespace): Type, FieldInfo, MethodInfo, ConstructorInfo, PropertyInfo, EventInfoe classe abstracta MemberInfo que é tipo base dos anteriores
  • Correspondência com os tipos do java.lang.reflect: Class, Member, Method e Field.
  • Tipo Assembly como representante da informação de um componente em .Net;
  • Assembly::LoadFrom(String path) -- Carregamento programático de assemblies de um path
  • Assembly::GetTypes() -- tipos constituintes de um Assembly;
  • Informação de um tipo: BaseType, GetInterfaces();
  • GetMembers() e GetMember(String), GetFields() e GetField(String);
  • ParameterInfo e propriedade ParameterType.
  • Definição de propriedades -- resultado da compilação e metadata.
  • Auto-Implemented Properties
  • API de Reflexão sobre propriedades
  • Especificação de um Logger para print do estado de um objecto (valor das propriedades de instância);
    • validar com os tipos Student, Point e Account.
  • Resolução dos TPCS:
    • Realizar em Java a implementação equivalente ao programa App01-hierarchy.cs
    • Implemente a classe Logger com o método estático Log(object) para print do valor das propriedades de instância.
  • BindingFlags: Public, NonPublic, Instance, Static
  • MethodInfo: GetParameters() e ParameterInfo
  • Chamada a métodos via reflexão através do Invoke(object, object[])

  • Custom attributes
  • Papel dos custom attributes como forma de expressão de intenções.
  • Definição de novos atributos:
    • Parâmetros de atributos com e sem nome;
    • Persistência dos custom attributes na metadata.
  • AttributeUsageAttribute => restrição sobre o target e AllowMultiple
  • Comparação entre custom attibutes e anotações Java
  • Inspecção de custom attributes em tempo de execução
  • IsDefined() e GetCustomAttributes()
  • Extensão da API de Logger com custom attributes:
    • IgnoreAttribute -- para excluir uma propriedade do método Log()
    • LayoutAttribute -- para customização da apresentação de valor de uma propriedade.
  • v1 -- passagem de uma string com o template de formatação do valor.
  • v2 -- passagem de Type a LayoutAttribute com a estratégia de formatação -- IFormat.
  • Reflexão: verificação de compatibilidade entre tipos (Cap 4.3 Essential .Net):
    • IsSubclassOf e IsAssignableFrom
  • Criação de instância via reflexão Activator.CreateInstance(Type)
  • Resolução do TPC
  • !!!! Alerta aos "custos" da API Reflexão
  • !!!!!Alerta para a repetição na obtenção do custom attribute na chamada do método Format()!!!!
  • Passar a obtenção do custom attribute no construtor

  • Suporte do Logger para Arrays
  • API de Reflexão para Arrays: Type::IsArray, Type::GetElementType(), Array::GetValue
  • Tipos referência são instanciados com o operador Csharp new => IL newobj
  • Detalhe da instrução newobj - responsabilidade na instanciação:
    1. alocação em memória do espaço necessário para alojar o objecto.
    2. inicialização do header e inicialização do restante espaço a zero
    3. chamada ao construtor
  • Tipos Valor NÃO são instanciados, mas apenas inicializados:
    • Chamada ao construtor sem parâmetros de um tipo valor gera um initobj (inicializa os campos a 0/null)
  • Instrução IL ldloca
  • Exemplificação de ldloca e initobj
  • Sendo Point uma struct então a chamada a new Point(8, 9) NÃO gera initobj, mas apenas a chamada ao construtor.
  • Uma struct não pode definir um construtor sem parametros, apenas com parâmetros.

Tipos em runtime (Cap 4.1 Essential .Net)


  • Tipos de conversão entre tipos: casting e coerção
  • casting implicito vs explicito e análise do codigo IL
  • Detalhe do operador IL castclass:
    • recebe dois parâmetros: referência no topo do stack + token do tipo destino da conversão.
    • em caso de sucesso deixa a referência no topo do stack
    • em caso de insucesso dá excepção.
  • operadores Csharp as
  • operadores IL isinst
  • operadores Csharp is
  • dr["Region"] is DBNull ? null : (string)dr["Region"] -> isinst + castclass <=> dr["Region"] as String; ++ eficiente , so tem isinst

  • Conversão entre Tipos Referencia e Valor -- boxing e unboxing.
  • Overhead da operação de boxing:
    1. Alocação de memória;
    2. Inicialização do header do objecto;
    3. Cópia do valor para o heap;
    • ... e futura libertação de memória pelo Garbage Collector
  • Detalhe da operação de unboxing
  • Tipo System.Object - métodos de instância virtuais (ToString, Equals, GetHashCode e Finalize) e não virtuais (GetType);
  • Tabela de métodos e COREINFO_CLASS_STRUCT
  • Evidenciar o this como o 1º argumento passado a um método de instância
  • Detalhe do código assembly gerado para despacho estático (endereço do método) e despacho dinâmico (baseado em offset do método)
  • Métodos de instância requerem uma referência para a instância chamada -- target;
  • Abordagem do compilador do C# na geração de call ou callvirt -- este último sempre usado para métodos de instância para verificar se o target é diferente de null.
  • Demonstração da verificação feita por callvirt.
  • Modelo de objectos de System.Reflection.Emit
    • AssemblyBuilder, ModuleBuilder, TypeBuilder, MethodBuidler e ILGenerator
  • Implementação de um exemplo de geração dinâmica de uma class conforme AssemblyBuilder

  • Implementação do logger versão 4 Logger-v4-dynamic.cs que elimina o uso de reflexão na leitura de valores das propriedades => substitui Getter por implementação dinâmica de IGetter.
  • Implementar um exemplo de um GetterStudentNr que estende IGetter como guião do código IL a gerar.
  • Abordagem:
    1. Princípio do uso de Emit: “Usar Reflexão para gerar IL” e "NÃO gerar IL que faz Reflexão”.
    2. Implementar uma classe em C# hard-coded à imagem do código que se quer gerar dinamicamente. Compilar e usar esse IL como base. E.g. GetterStudentNr, GetterPointX, ou outro.
    3. Usar ferramenta PEVerify.exe para validar a conformidade dos componentes gerados.
  • Revisão da tradução de call e callvirt em despacho estático (DE) ou dinâmico (DD):
static instância virtual
call DE DE (struct) DE (base)
callvirt -- DE DD
  • Demonstração da chamada a um método da base virtual com a instrução callvirt, resultando num ciclo infinito.

  • Atributos de métodos: virtual, new e override
  • Exemplos do resultado do layout da tabela de métodos com a utilização dos atributos anteriores
  • Estudo de casos de redifinição (override) ou "esconder" (new) de métodos => efeitos no layout da tabela de métodos e resultado na chamada a esses métodos.
  • Introdução ao conceito de Sequência, e.g. [], IList, IEnumerable, ..
  • Implementação de funções utilitárias sobre sequências: convert e filter
  • Características de uma sequência:
    1. Traverse (e.g. MoveNext()) + Access (e.g. Current)
    2. Operações:
      • Intermédias, e.g. map(), filter(), limit(): Seq -> Seq
      • Terminais, e.g. first(), max(): Seq -> R | void
    3. Lazy -- elementos computados on demand

  • Exercício: Dado um IEnumerable com as linhas de um ficheiro, listar o primeiro nome de todos os alunos com número superior a 42000 e primeira letra J.
  • Versão 1: implementar as funções ConvertToStudents, ConvertToName, FilterNameStartsWith e FilterWithNumberGreaterThan.
  • Versão 2: eliminar a dependência do modelo de domínio criando um único conversor e uma única filtragem:
    • IEnumerable Convert(IEnumerable src, Function func)
    • IEnumerable Filter(IEnumerable src, Predicate test)

Definição das interfaces Function e Predicate:

public interface Predicate {
    bool Invoke(object item);
}
public interface Function {
    object Invoke(object item);
}
  • Versão 3: Usar delegate em vez de interface para definição de Function e Predicate.

  • Introdução às Expressões Lambda como representação de métodos anónimos:
    • resulta na criação de um novo método com um nome atribuído pelo compilador (método anónimo)
  • A utilização de expressões lambda evidencia a parametrização de comportamento onde é visível a passagem explícita de código por parâmetro:

Filter(..., item => ((Student) item).nr > 41000 ))

  • Separação entre lógica de iteração e lógica do predicado;
  • Expressão Lambda = expressão que representa uma implementação concisa de um método anónimo.
  • Sintaxe das Expressões Lambda
  • Inferência dos parâmetros de tipo da Expressão Lambda
  • Distinção entre processamento Eager e Lazy
  • Lazy => on-demand processing
  • Lazy => a execução de operações intermédias não processa a sequência.
  • Detalhe de IEnumerable e IEnumerator
  • Versão 4: Implementação Lazy para Converter e Filter
  • Análise da intercalação da execução das operações na versão Lazy
  • Implementação de uma operação Take()
  • Análise do número total de operações executadas.
  • Genéricos:
    • Classes genéricas – classes com parâmetros de tipo
    • Métodos genéricos – métodos com parâmetros de tipo.
  • Inferência dos argumentos de tipo a partir dos parâmetros actuais.
  • Abordagens diferentes nos genéricos:
    • Java: A<Object> <=> A
    • .Net: A<Object> != A ---> Tipos diferentes e incompatíveis;
      • workaround fazer A<T> ----|> A, ou seja class A<T> : A {…}

  • Definição dos métodos genéricos: Convert<T, R>() e Filter<T>()
  • Delegates: Function<T,R> e Predicate<T>
  • Aplicação de EIMI na implementação de IEnumerable<T> e IEnumerator<T>
  • EIMI - Explicit Interface Method Implementations
  • Mais valias na utilização de EIMI em casos de definição dos métodos como privados.
  • Resolução do TPC
  • Exercício: Implementar IEnumerable<T> generate<T>(Func<T> generator)
  • Exemplo:
int n = 0;
generate(() => n++); // 0, 1, 2, 3, ...
  • Sequências Inifinitas

  • instrução yield
  • Vantagens de uma API fluente na legibilidade
  • Extension Methods => não usar nos tipos de nossa autoria.
  • Métodos utilitários de System.Linq
  • Análise do código resultante da compilação da definição e utilização de um delegate
  • Uma instância de um delegate contém internamente um ponteiro para uma função.
  • Delegate como uma forma type safe de usar um ponteiro para uma função.
  • O método encapsulado num delegate pode ser chamado indirectamente através desse delegate. E.g. seja Function f então podemos fazer f.Invoke(n).
  • Em C# escrever f.Invoke(n) é equivalente a f(n)
  • Campos _methodPtr e _target herdados da classe Delegate
  • Instrução IL ldftn
  • Construtor de um tipo delegate recebe os dois parâmetros que inicializarão os campos anteriores:
    • new Function(App.Foo); <=> App.Foo
  • Diferença de instanciar um tipo delegate para um método estático ou de instância.

Pretende-se desenvolver uma biblioteca para definição e validação de regras sobre propriedades de objectos. Uma regra é representada pela interface IValidation { bool Validate(object obj); } (exemplos de implementações desta interface: Above18, NotNull).

Uma instância de Validator<T> representa validadores para uma entidade de domínio de tipo T, aos quais é possível acrescentar regras de validação para determinadas propriedade (identificada pelo seu nome), tal como apresentado no exemplo seguinte:

    class Student {
      public int Age;
      public String Name;
    } 
    Student s = new Student(); s.Age = 20; s.Name = "Anacleto";
    Validator<Student> validator = new Validator<Student>()
                    .AddValidation("Age", new Above18())
                    .AddValidation("Name", new NotNull());
    validator.Validate(s);
  1. [1] Implemente a classe Above18, que retorna true se o valor recebido por parâmetro for superior a 18, ou false caso contrário.
  2. [2] Implemente a classe Validator<T> tendo em conta que o método Validate lança a exceção ValidationException, se falhar alguma das regras.
  3. [2] Sem alterar o código escrito anteriormente, acrescente o necessário para que seja suportada a adição de regras na forma de delegates do tipo Func<W, bool>, conforme demonstra o exemplo seguinte. Se a propriedade indicada não for do tipo W, então é lançada a exceção TypeMismatchException.
  4. [2] Adicione o necessário e modifique apenas o método AddValidation(string name, IValidation val) de modo a que sobre a mesma propriedade possam ser adicionados vários validadores.

TPC: Realizar o Exercício 2

  • Problemas na gestão de memória manual (e.g. com malloc() e free()): memmory leaks e object corruption
  • Desvantagens: lookup no malloc, Aumento do espaço de endereçamento + fragmentação.
  • Gestão automática de memória. VANTAGENS:
    • SEM fragmentação;
    • RAPIDEZ na alocação de memória.
    • Working Set vai ser mais pequeno
    • !!!! custo na libertação de memória.
  • Algoritmo de GC baseado em gerações e compactação.
  • Root references;
  • classe GC e conjunto de métodos para controlo do Garbage Collector:
    • GC::GetGeneration(object)
    • GC::Collect()
    • GC::Collect(int gen) -- até à geração gen
    • GC::GetTotalMemory(bool forceFullCollection)
  • Tipos que representam (wrappers) para recursos nativos, tais como: ficheiros, sockets, etc, requerem libertação dos handles para esses recursos.
  • Método Finalize.
  • Detalhes do processo de Finalização de objectos:
    • Finalization List e Freachable list
  • GC.WaitForPendingFinalizers
  • Interface IDisposable e método Dispose(). Analogia com a interface Autocloseable do Java
  • Idioma com utilização do using do Csharp. Seu equivalente no Java com try-with-resources