UC00620

Programação em C#

.NET, OOP, LINQ, Entity Framework

Curso profissional · 50h · Linguagem Microsoft

Plano

  1. Setup .NET
  2. Sintaxe C#
  3. OOP em C#
  4. Colecções e LINQ
  5. Excepções
  6. Ficheiros e I/O
  7. Entity Framework (BD)
  8. Projecto final

Bloco 1 · .NET

O que é C# e .NET

  • C#: linguagem da Microsoft (criada 2002).
  • .NET: plataforma multi-linguagem (C#, F#, VB.NET).
  • .NET 8+ (LTS): cross-platform (Windows/Linux/Mac).
  • Usado em: backend (ASP.NET), Unity (jogos), apps Windows.

Sintaxe parecida com Java e C++.

Setup

# Instalar .NET SDK 8 (LTS)
# https://dotnet.microsoft.com/download

dotnet --version
# 8.0.x

# Criar projecto
dotnet new console -n MinhaApp
cd MinhaApp
dotnet run

IDE: Visual Studio (Windows), Rider (multi), VSCode.

Hello World

// Program.cs (estilo moderno, top-level statements)
Console.WriteLine("Olá mundo!");

string nome = "Ana";
Console.WriteLine($"Olá, {nome}");
dotnet run

Top-level statements (.NET 6+): sem class Program { static void Main }.

Bloco 2 · Sintaxe

Tipos

int idade = 25;
long populacao = 8_000_000_000L;
double preco = 9.99;
decimal valor = 100.50m;  // financeiro
bool activo = true;
char letra = 'A';
string nome = "Ana";

// var (inferência)
var x = 42;          // int
var nome2 = "Ana";   // string

// Nullable
int? idade2 = null;
string? nome3 = null;

Operadores e strings

// Aritméticos: + - * / %
int soma = 5 + 3;
double real = 10.0 / 3;  // 3.333

// Interpolação (preferido)
string saudacao = $"Olá, {nome}, tens {idade} anos";

// Concatenação
string s = "Olá " + nome;

// Métodos
nome.ToUpper();
nome.Length;
nome.Substring(0, 3);
nome.Contains("a");
nome.Replace("a", "e");

Estruturas controlo

// If
if (idade >= 18) {
    Console.WriteLine("Adulto");
} else {
    Console.WriteLine("Menor");
}

// Switch expression (moderno)
string tipo = dia switch {
    1 or 2 or 3 or 4 or 5 => "Útil",
    6 or 7 => "Weekend",
    _ => "Inválido"
};

// Pattern matching
if (obj is string s && s.Length > 5) {
    Console.WriteLine(s);
}

Loops

// For
for (int i = 0; i < 10; i++) {
    Console.WriteLine(i);
}

// Foreach
string[] frutas = { "maçã", "uva", "pera" };
foreach (var f in frutas) {
    Console.WriteLine(f);
}

// While / do-while
int x = 0;
while (x < 5) { x++; }

Bloco 3 · OOP

Classe

public class Pessoa
{
    // Propriedade auto (get/set)
    public string Nome { get; set; }
    public int Idade { get; set; }
    
    // Construtor
    public Pessoa(string nome, int idade)
    {
        Nome = nome;
        Idade = idade;
    }
    
    // Método
    public void Apresentar()
    {
        Console.WriteLine($"Olá, sou {Nome}");
    }
}

// Uso
var p = new Pessoa("Ana", 25);
p.Apresentar();

Propriedades vs campos

public class Conta
{
    // Campo privado
    private double _saldo;
    
    // Propriedade com lógica
    public double Saldo
    {
        get { return _saldo; }
        set 
        { 
            if (value < 0) throw new ArgumentException("Negativo");
            _saldo = value;
        }
    }
    
    // Propriedade auto (compilador cria campo)
    public string Titular { get; set; }
    
    // Read-only
    public DateTime Criada { get; }
}

Herança

public class Empregado : Pessoa
{
    public double Salario { get; set; }
    
    public Empregado(string nome, int idade, double salario)
        : base(nome, idade)  // chama construtor pai
    {
        Salario = salario;
    }
    
    public override void Apresentar()
    {
        base.Apresentar();
        Console.WriteLine($"Salário: {Salario}€");
    }
}

virtual / override para polimorfismo.

Interfaces

public interface IAnimal
{
    void EmitirSom();
    string Nome { get; set; }
}

public class Cao : IAnimal
{
    public string Nome { get; set; }
    
    public void EmitirSom() {
        Console.WriteLine("Au au!");
    }
}

Convenção: nomes de interfaces começam com I.

Records (C# 9+)

// Imutável, value-based equality, ToString automático
public record Ponto(double X, double Y);

var p1 = new Ponto(1, 2);
var p2 = new Ponto(1, 2);
Console.WriteLine(p1 == p2);  // true (value equality!)
Console.WriteLine(p1);        // Ponto { X = 1, Y = 2 }

// With expression (cópia com modificação)
var p3 = p1 with { Y = 5 };

Bloco 4 · Colecções e LINQ

List, Dictionary, HashSet

using System.Collections.Generic;

// List
var nomes = new List<string> { "Ana", "Bruno" };
nomes.Add("Carla");
nomes.Remove("Ana");
Console.WriteLine(nomes.Count);

// Dictionary
var idades = new Dictionary<string, int> {
    ["Ana"] = 25,
    ["Bruno"] = 30
};
Console.WriteLine(idades["Ana"]);

// HashSet (sem duplicados)
var unicos = new HashSet<int> { 1, 2, 3, 1 };
// {1, 2, 3}

LINQ — Language Integrated Query

using System.Linq;

var numeros = new List<int> { 1, 2, 3, 4, 5, 6 };

// Method syntax (preferido)
var pares = numeros.Where(n => n % 2 == 0).ToList();
var dobrados = numeros.Select(n => n * 2).ToList();
var soma = numeros.Sum();
var max = numeros.Max();

// Query syntax (alternativa SQL-like)
var resultado = from n in numeros
                where n > 2
                orderby n descending
                select n * 2;

LINQ avançado

var produtos = new List<Produto> { ... };

// Filter + Map + GroupBy
var caros = produtos
    .Where(p => p.Preco > 100)
    .OrderByDescending(p => p.Preco)
    .Take(5)
    .Select(p => new { p.Nome, p.Preco })
    .ToList();

// Agrupar
var porCategoria = produtos
    .GroupBy(p => p.Categoria)
    .Select(g => new { 
        Categoria = g.Key, 
        Total = g.Sum(p => p.Preco) 
    });

Bloco 5 · Excepções

Try-catch

try
{
    int[] arr = { 1, 2, 3 };
    Console.WriteLine(arr[10]);
}
catch (IndexOutOfRangeException ex)
{
    Console.WriteLine($"Erro: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"Outro: {ex.Message}");
}
finally
{
    Console.WriteLine("Sempre executa");
}

Throw e custom

public void Sacar(double valor)
{
    if (valor <= 0)
        throw new ArgumentException("Valor inválido", nameof(valor));
    
    if (valor > _saldo)
        throw new SaldoInsuficienteException($"Saldo: {_saldo}");
    
    _saldo -= valor;
}

public class SaldoInsuficienteException : Exception
{
    public SaldoInsuficienteException(string message) 
        : base(message) { }
}

Bloco 6 · Ficheiros

Ler e escrever

using System.IO;

// Ler tudo
string conteudo = File.ReadAllText("dados.txt");

// Ler linhas
string[] linhas = File.ReadAllLines("dados.txt");
foreach (var l in linhas) Console.WriteLine(l);

// Escrever (substitui)
File.WriteAllText("output.txt", "Olá mundo");

// Escrever linhas
File.WriteAllLines("output.txt", new[] { "linha 1", "linha 2" });

// Append
File.AppendAllText("log.txt", "Nova entrada\n");

// Existe?
bool existe = File.Exists("dados.txt");

JSON

using System.Text.Json;

// Serializar
var pessoa = new Pessoa("Ana", 25);
string json = JsonSerializer.Serialize(pessoa);
File.WriteAllText("pessoa.json", json);

// Desserializar
string lido = File.ReadAllText("pessoa.json");
var p = JsonSerializer.Deserialize<Pessoa>(lido);

// Indented
var opts = new JsonSerializerOptions { WriteIndented = true };
string jsonBonito = JsonSerializer.Serialize(pessoa, opts);

Bloco 7 · Entity Framework

Setup EF Core

dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet tool install --global dotnet-ef

Modelo + DbContext

public class Produto
{
    public int Id { get; set; }
    public string Nome { get; set; } = "";
    public double Preco { get; set; }
}

public class AppDbContext : DbContext
{
    public DbSet<Produto> Produtos { get; set; }
    
    protected override void OnConfiguring(DbContextOptionsBuilder o)
    {
        o.UseSqlite("Data Source=loja.db");
    }
}

Migrations e CRUD

dotnet ef migrations add InicialDB
dotnet ef database update
using var db = new AppDbContext();

// Create
db.Produtos.Add(new Produto { Nome = "Caneta", Preco = 1.50 });
db.SaveChanges();

// Read
var todos = db.Produtos.ToList();
var caros = db.Produtos.Where(p => p.Preco > 10).ToList();

// Update
var p = db.Produtos.Find(1);
if (p != null) {
    p.Preco = 2.00;
    db.SaveChanges();
}

// Delete
db.Produtos.Remove(p);
db.SaveChanges();

Bloco 8 · Async / await

Programação assíncrona

public async Task<string> DownloadAsync(string url)
{
    using var client = new HttpClient();
    string content = await client.GetStringAsync(url);
    return content;
}

// Uso
string html = await DownloadAsync("https://example.com");
Console.WriteLine(html.Length);

async + await: torna I/O não-bloqueante (essencial em web/desktop).

UC00620 · resumo

  • C# é tipado, OOP, moderno (records, patterns, nullable).
  • .NET 8 cross-platform (Windows/Linux/Mac).
  • Propriedades com get/set, auto e com lógica.
  • LINQ = poder funcional sobre colecções.
  • EF Core = ORM elegante (Code-first com migrations).
  • async/await para I/O sem bloquear.
  • Base para ASP.NET Core (web), MAUI (mobile), Unity (jogos).

NOTAS DO PROFESSOR 📖 C# e a plataforma .NET Importa separar dois conceitos: C# é a linguagem, .NET é a plataforma e o conjunto de bibliotecas onde corre. A grande mudança recente foi tornar-se verdadeiramente multi-plataforma, deixando de estar preso ao Windows. A semelhança com Java facilita a vida a quem já conhece uma das duas. 🗣️ Pontos a desenvolver oralmente • Clarificar a relação linguagem (C#) vs plataforma (.NET). • Realçar que .NET 8 corre em Linux e Mac, não só Windows. • Mapear os domínios de uso: web (ASP.NET), jogos (Unity), desktop.

NOTAS DO PROFESSOR 📖 A CLI dotnet como centro de comando A ferramenta `dotnet` cria, compila, corre e gere pacotes do projeto a partir do terminal. Aprender estes comandos torna os formandos independentes da IDE e prepara-os para ambientes de automação e CI. Convém criar e correr um projeto em conjunto na sala. 🗣️ Pontos a desenvolver oralmente • Demonstrar `dotnet new`, `dotnet run` e `dotnet build`. • Explicar a diferença entre o SDK (desenvolvimento) e o runtime (execução). • Referir que a mesma CLI funciona igual nos três sistemas operativos.

NOTAS DO PROFESSOR 📖 Menos cerimónia para começar Os top-level statements eliminam o boilerplate da classe Program e do método Main, deixando escrever código logo no ficheiro. Convém explicar que esse esqueleto continua a existir — o compilador gera-o por nós. A interpolação com `$"..."` é a forma moderna de compor strings. 🗣️ Pontos a desenvolver oralmente • Mostrar o que o compilador gera por trás dos top-level statements. • Comparar com o Hello World clássico de Java para realçar a simplificação. • Introduzir a interpolação de strings com `$`.

NOTAS DO PROFESSOR 📖 Tipos e a segurança dos nulls C# é estaticamente tipado como Java, mas acrescenta tipos nullable (`int?`, `string?`) que tornam explícito quando um valor pode ser nulo. O `decimal` é importante: usa-se para dinheiro porque evita os erros de arredondamento do `double`. Esta escolha tem impacto real em aplicações financeiras. 🗣️ Pontos a desenvolver oralmente • Justificar `decimal` para valores monetários com um exemplo de erro de `double`. • Explicar o que o `?` acrescenta a um tipo (permite null). • Mostrar o `var` e quando a inferência é apropriada.

NOTAS DO PROFESSOR 📖 Manipular texto com a biblioteca de strings A interpolação `$"..."` deve ser a forma preferida de juntar variáveis a texto pela legibilidade. Os métodos de string devolvem sempre uma nova string (são imutáveis), tal como em Java e Python. Convém mostrar que `Length` é propriedade e os outros são métodos. 🗣️ Pontos a desenvolver oralmente • Comparar interpolação com concatenação por `+`. • Reforçar a imutabilidade: `Replace` não muda a original. • Notar a convenção PascalCase nos métodos da biblioteca .NET.

NOTAS DO PROFESSOR 📖 Switch expression e pattern matching C# moderno transformou o switch numa expressão que devolve valor, mais conciso e seguro. O pattern matching com `is` permite testar o tipo e ao mesmo tempo capturar a variável já convertida, eliminando casts manuais. São funcionalidades que tornam o código mais expressivo. 🗣️ Pontos a desenvolver oralmente • Comparar o switch expression com o if-else equivalente. • Demonstrar o pattern matching `is string s` e o que poupa em código. • Mostrar o uso do descarte `_` como caso por omissão.

NOTAS DO PROFESSOR 📖 Os ciclos clássicos A sintaxe dos ciclos em C# é praticamente idêntica à de Java e C, o que é uma vantagem para quem migra. O `foreach` é o mais legível para percorrer coleções. Convém reforçar a escolha do ciclo certo conforme se precise ou não do índice. 🗣️ Pontos a desenvolver oralmente • Mostrar quando preferir `foreach` ao `for` indexado. • Distinguir `while` de `do-while` (este corre sempre uma vez). • Ligar o `foreach` às coleções e ao LINQ que vêm a seguir.

NOTAS DO PROFESSOR 📖 Classes e propriedades automáticas A grande diferença face a Java são as propriedades: `{ get; set; }` substitui o par getter/setter manual com uma sintaxe muito mais limpa. O construtor inicializa o estado e os métodos definem comportamento. Convém realçar a convenção PascalCase para membros públicos em C#. 🗣️ Pontos a desenvolver oralmente • Contrastar a propriedade auto de C# com os getters/setters de Java. • Explicar o construtor e a criação de instâncias com `new`. • Notar que em C# as propriedades públicas usam PascalCase.

NOTAS DO PROFESSOR 📖 Campos, propriedades e validação Um campo guarda o valor; uma propriedade controla o acesso. A propriedade com lógica no `set` permite validar antes de gravar (rejeitar saldo negativo), enquanto a auto-propriedade gera o campo por nós. As read-only (só `get`) protegem valores que não devem mudar após a criação. 🗣️ Pontos a desenvolver oralmente • Mostrar a propriedade com validação no `set` como exemplo de encapsulamento. • Distinguir campo privado de propriedade pública. • Explicar o uso de propriedades read-only para imutabilidade parcial.

NOTAS DO PROFESSOR 📖 Herança explícita com virtual/override Ao contrário de Java, em C# um método só pode ser sobreposto se for marcado `virtual` na classe-mãe; a subclasse usa `override`. Esta explicitude evita sobreposições acidentais. O `base` chama a versão da mãe e o `: base(...)` no construtor encadeia a inicialização. 🗣️ Pontos a desenvolver oralmente • Explicar porque é preciso `virtual` para permitir `override` (diferença face a Java). • Mostrar o encadeamento de construtores com `: base(...)`. • Demonstrar polimorfismo com uma coleção de objetos de tipos diferentes.

NOTAS DO PROFESSOR 📖 Contratos com a convenção do I As interfaces em C# definem o contrato que as classes implementam, podendo incluir propriedades além de métodos. A convenção de prefixar com `I` (IAnimal) é universal no ecossistema .NET e ajuda a identificar interfaces de imediato no código. Uma classe pode implementar várias. 🗣️ Pontos a desenvolver oralmente • Reforçar a convenção do prefixo `I` e porque é útil. • Mostrar que uma interface pode exigir propriedades, não só métodos. • Ligar interfaces à injeção de dependências usada em ASP.NET.

NOTAS DO PROFESSOR 📖 Records para dados imutáveis O record é um tipo pensado para guardar dados: imutável por omissão, com comparação por valor e ToString automático. Resolve em uma linha o que noutras linguagens exige muito código repetitivo. A expressão `with` cria uma cópia modificada sem alterar o original. 🗣️ Pontos a desenvolver oralmente • Contrastar a igualdade por valor do record com a igualdade por referência das classes. • Mostrar o `with` para criar variantes sem mutação. • Dar exemplos de quando usar record (DTOs, dados de configuração).

NOTAS DO PROFESSOR 📖 As três coleções essenciais List (ordenada, com duplicados), Dictionary (chave→valor) e HashSet (sem duplicados) cobrem a maioria das necessidades. A sintaxe de inicialização com chavetas é prática. A escolha entre elas deve guiar-se pelas mesmas perguntas vistas noutras linguagens: ordem, unicidade, acesso por chave. 🗣️ Pontos a desenvolver oralmente • Mapear cada coleção C# ao seu equivalente em Java/Python. • Mostrar a sintaxe de inicialização com índices no Dictionary. • Reforçar a decisão List vs Dictionary vs HashSet.

NOTAS DO PROFESSOR 📖 Consultar coleções como se fossem bases de dados O LINQ é uma das joias do C#: permite filtrar, transformar e agregar dados com uma sintaxe uniforme, seja sobre listas em memória ou sobre uma base de dados via EF Core. As duas sintaxes (method e query) são equivalentes — convém dominar a method syntax por ser a mais usada. 🗣️ Pontos a desenvolver oralmente • Mostrar a equivalência entre as duas sintaxes com o mesmo exemplo. • Explicar a avaliação preguiçosa: a query só corre quando se enumera. • Antecipar que o mesmo LINQ servirá para consultar o EF Core mais à frente.

NOTAS DO PROFESSOR 📖 Encadear operações para consultas ricas Combinar Where, OrderBy, Take, Select e GroupBy permite expressar consultas complexas de forma legível. O GroupBy resolve em poucas linhas o que seria um ciclo aninhado trabalhoso. Os tipos anónimos (`new { ... }`) são úteis para moldar o resultado à medida. 🗣️ Pontos a desenvolver oralmente • Decompor a pipeline operação a operação, mostrando o resultado intermédio. • Explicar os tipos anónimos e quando são apropriados. • Dar um caso de negócio para o GroupBy (totais por categoria).

NOTAS DO PROFESSOR 📖 Tratamento de erros em C# O modelo try/catch/finally é semelhante ao de Java, mas C# não tem checked exceptions — nenhuma exceção é obrigatória declarar. Isto dá flexibilidade mas exige disciplina para não ignorar falhas. Apanhar a exceção mais específica antes da genérica continua a ser boa prática. 🗣️ Pontos a desenvolver oralmente • Destacar que C# não obriga a declarar exceções, ao contrário de Java. • Explicar a ordem dos catch do específico para o genérico. • Mostrar o papel do `finally` para limpar recursos.

NOTAS DO PROFESSOR 📖 Validar e sinalizar erros de negócio Lançar exceções com `throw` permite interromper logo uma operação inválida. O `nameof(valor)` produz o nome do parâmetro como texto, evitando strings mágicas que ficam desatualizadas. Exceções personalizadas dão semântica de negócio (SaldoInsuficiente) muito mais clara do que erros genéricos. 🗣️ Pontos a desenvolver oralmente • Explicar a vantagem de `nameof` face a escrever o nome à mão. • Justificar a criação de exceções personalizadas. • Mostrar a validação de argumentos no início do método (guard clauses).

NOTAS DO PROFESSOR 📖 I/O simples com a classe File A classe `File` oferece métodos estáticos diretos para ler e escrever sem gerir streams manualmente. Convém o mesmo alerta de sempre: `WriteAllText` substitui todo o conteúdo, enquanto `AppendAllText` acrescenta. Verificar `File.Exists` antes de ler evita exceções. 🗣️ Pontos a desenvolver oralmente • Avisar sobre a substituição de conteúdo com WriteAll vs AppendAll. • Mostrar a verificação de existência antes de operar sobre o ficheiro. • Referir caminhos relativos vs absolutos e onde o ficheiro é criado.

NOTAS DO PROFESSOR 📖 Converter objetos em texto e vice-versa A serialização JSON transforma objetos C# em texto que se pode guardar ou enviar, e a desserialização faz o caminho inverso. O `System.Text.Json` é a solução integrada no .NET, sem precisar de pacotes externos. É a base da comunicação entre aplicações e APIs. 🗣️ Pontos a desenvolver oralmente • Distinguir serializar (objeto→texto) de desserializar (texto→objeto). • Mostrar a opção `WriteIndented` para JSON legível em desenvolvimento. • Ligar JSON à comunicação com APIs web.

NOTAS DO PROFESSOR 📖 O ORM da plataforma .NET O Entity Framework Core é um ORM: deixa trabalhar com objetos C# em vez de escrever SQL à mão, gerando as queries automaticamente. Aqui usa-se SQLite por ser um ficheiro local sem servidor, ideal para aprender. Os pacotes instalam o provedor da base de dados e as ferramentas de migração. 🗣️ Pontos a desenvolver oralmente • Explicar o conceito de ORM e o que ele poupa face ao JDBC manual de Java. • Justificar SQLite como base de dados de aprendizagem (sem servidor). • Referir que o mesmo EF Core suporta SQL Server, PostgreSQL e outros.

NOTAS DO PROFESSOR 📖 Mapear classes a tabelas A abordagem code-first define o modelo de dados em classes C#, e o EF Core encarrega-se de criar as tabelas. O `DbContext` é a sessão com a base de dados, e cada `DbSet` representa uma tabela. A propriedade `Id` torna-se chave primária por convenção. 🗣️ Pontos a desenvolver oralmente • Explicar a abordagem code-first: o modelo gera o esquema, não o contrário. • Identificar o papel do DbContext como ponte com a base de dados. • Notar as convenções (Id como chave primária) que reduzem configuração.

NOTAS DO PROFESSOR 📖 Migrations e o ciclo CRUD As migrations versionam a evolução do esquema da base de dados, permitindo aplicar mudanças de forma controlada. O CRUD (Create, Read, Update, Delete) faz-se com objetos C# e LINQ, e nada é persistido até chamar `SaveChanges`. Reparar que a leitura usa o mesmo LINQ visto antes. 🗣️ Pontos a desenvolver oralmente • Explicar o que é uma migration e porque versionar o esquema. • Sublinhar que `SaveChanges` é o que confirma as alterações na base de dados. • Ligar o `Where` da leitura ao LINQ aprendido no bloco 4.

NOTAS DO PROFESSOR 📖 Não bloquear à espera de I/O O par async/await permite que o programa não fique parado à espera de operações lentas (rede, disco), libertando a thread para outro trabalho. É essencial em aplicações web (que servem muitos pedidos) e desktop (para não congelar a interface). Convém explicar que `await` não cria threads — coordena tarefas. 🗣️ Pontos a desenvolver oralmente • Distinguir assíncrono de paralelo: await não significa correr em paralelo. • Mostrar o problema concreto que resolve: interface congelada ou servidor bloqueado. • Referir a regra de propagar `async` pela cadeia de chamadas.

NOTAS DO PROFESSOR 📖 Consolidar o percurso em C# Este resumo destaca o que distingue C#: propriedades, records, LINQ, EF Core e async/await — funcionalidades modernas que tornam o código conciso e seguro. Vale a pena ligar cada uma aos destinos de carreira: ASP.NET para backend web, Unity para jogos, MAUI para mobile. 🗣️ Pontos a desenvolver oralmente • Pedir aos formandos que comparem C# com Java, identificando as diferenças-chave. • Apontar os caminhos de aprofundamento conforme o interesse de cada um. • Reforçar que LINQ e async/await são competências muito valorizadas no mercado .NET.