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 ContadorStructcom campoint Valore métodoIncrementar()que soma 1. - Definir
class ContadorClasscom campoint Valore métodoIncrementar()que soma 1. - Criar um array
ContadorStruct[]e um arrayContadorClass[]com 3 elementos cada. - Percorrer os arrays com
for, chamarIncrementar()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
- Definimos
ContadorStructcom campoValore métodoIncrementar()que usaValor++. - Definimos
ContadorClasscom a mesma estrutura, mas como classe. - Criamos
ContadorStruct[] structscom 3 elementos. Como structs são tipos de valor, o array já contém instâncias comValor = 0. - No
for, chamamosstructs[i].Incrementar(). O compilador tratastructs[i]como variável (endereço no array) e modifica o elemento diretamente. Se tivéssemos feitovar s = structs[i]; s.Incrementar();, a cópia seria modificada e o array ficaria com zeros. - Imprimimos os valores: todos 1, pois cada elemento foi incrementado.
- Criamos
ContadorClass[] classes. Como classes são referências, o array começa comnull. Precisamos instanciar cada elemento comnew ContadorClass(). - Chamamos
classes[i].Incrementar(). A referência aponta para o objeto, então o objeto é alterado. - 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çaNullReferenceException. Corrija comclasses[i] = new ContadorClass();. - Tentar modificar struct via cópia:
var s = structs[0]; s.Incrementar();não altera o array. Usestructs[0].Incrementar()diretamente. - Usar
foreachpara incrementar structs:foreach (var s in structs) s.Incrementar();não compila (variável de iteração é somente leitura) ou não tem efeito. Usefor. - 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