Desafio C#

Escolhendo entre struct e class

Enunciado

Implemente duas versões de um contador: ContadorStruct (struct) e ContadorClass (class), ambos com um campo Valor e método Incrementar(). No Main, crie um array de cada tipo, incremente cada elemento e imprima os valores para demonstrar a diferença de comportamento em coleções.

Requisitos

  • Definir struct ContadorStruct com campo int Valor e método Incrementar() que soma 1.
  • Definir class ContadorClass com campo int Valor e método Incrementar() que soma 1.
  • Criar um array ContadorStruct[] e um array ContadorClass[] com 3 elementos cada.
  • Percorrer os arrays com for, chamar Incrementar() em cada elemento e imprimir os valores finais.

Código inicial

using System;

// Defina ContadorStruct e ContadorClass

class Program
{
    static void Main()
    {
        // Crie arrays, incremente e imprima
    }
}

Saída esperada

Struct: 1 1 1
Class: 1 1 1
Ver dica

Em arrays de struct, acessar arr[i] retorna uma cópia. Para modificar o elemento, chame o método diretamente no índice: arr[i].Incrementar() funciona porque o compilador trata o elemento como variável. Já em arrays de class, arr[i] é uma referência e Incrementar() altera o objeto original.

Mostrar solução
using System;

// Struct: tipo de valor
public struct ContadorStruct
{
    public int Valor;
    public void Incrementar() => Valor++;
}

// Class: tipo de referência
public class ContadorClass
{
    public int Valor;
    public void Incrementar() => Valor++;
}

class Program
{
    static void Main()
    {
        // Array de struct: cada elemento é uma cópia
        ContadorStruct[] structs = new ContadorStruct[3];
        for (int i = 0; i < structs.Length; i++)
            structs[i].Incrementar(); // modifica o elemento no array

        Console.Write("Struct: ");
        foreach (var s in structs)
            Console.Write($"{s.Valor} ");
        Console.WriteLine();

        // Array de class: cada elemento é uma referência
        ContadorClass[] classes = new ContadorClass[3];
        for (int i = 0; i < classes.Length; i++)
            classes[i] = new ContadorClass(); // precisa instanciar

        for (int i = 0; i < classes.Length; i++)
            classes[i].Incrementar(); // altera o objeto apontado

        Console.Write("Class: ");
        foreach (var c in classes)
            Console.Write($"{c.Valor} ");
        Console.WriteLine();
    }
}

Passo a passo

  1. Definimos ContadorStruct com campo Valor e método Incrementar() que usa Valor++.
  2. Definimos ContadorClass com a mesma estrutura, mas como classe.
  3. Criamos ContadorStruct[] structs com 3 elementos. Como structs são tipos de valor, o array já contém instâncias com Valor = 0.
  4. No for, chamamos structs[i].Incrementar(). O compilador trata structs[i] como variável (endereço no array) e modifica o elemento diretamente. Se tivéssemos feito var s = structs[i]; s.Incrementar();, a cópia seria modificada e o array ficaria com zeros.
  5. Imprimimos os valores: todos 1, pois cada elemento foi incrementado.
  6. Criamos ContadorClass[] classes. Como classes são referências, o array começa com null. Precisamos instanciar cada elemento com new ContadorClass().
  7. Chamamos classes[i].Incrementar(). A referência aponta para o objeto, então o objeto é alterado.
  8. Imprimimos: todos 1, mesmo resultado visual, mas o mecanismo é diferente.

Por que funciona

Em arrays de struct, os dados ficam inline no array. Acessar structs[i] retorna uma referência ao slot, permitindo modificação direta. Em arrays de class, cada slot guarda uma referência para um objeto na heap; modificar o objeto através da referência afeta o original. A saída é igual, mas o comportamento em cópias é diferente: se você fizer var copia = structs[0]; copia.Incrementar();, o array não muda; com class, mudaria.

Erros comuns

  • Esquecer de instanciar elementos do array de class: classes[i].Incrementar() lança NullReferenceException. Corrija com classes[i] = new ContadorClass();.
  • Tentar modificar struct via cópia: var s = structs[0]; s.Incrementar(); não altera o array. Use structs[0].Incrementar() diretamente.
  • Usar foreach para incrementar structs: foreach (var s in structs) s.Incrementar(); não compila (variável de iteração é somente leitura) ou não tem efeito. Use for.
  • Assumir que structs são sempre mais rápidas: para tipos grandes, a cópia pode ser custosa. Meça antes de otimizar.

Outra forma de resolver

Usar ref para acessar elementos do array de struct:

for (int i = 0; i < structs.Length; i++)
{
    ref ContadorStruct s = ref structs[i];
    s.Incrementar();
}

Isso evita cópias e deixa explícito que estamos modificando o elemento. É preferível quando a struct é grande ou quando se deseja clareza sobre a mutação in-place.

Saída esperada

Struct: 1 1 1 
Class: 1 1 1