Olá Luiz! Tudo bem?
Há algumas diferenças importantes entre classes abstratas e interfaces.
A primeira (e crucial) é que interfaces não estabelecem relação de herança, isso significa que quando você implementa uma interface, sua classe não herda da interface, ou seja, não há uma relação é um como há com classes abstratas.
No .Net (como no Java) não há herança múltipla, só é possível uma classe herdar de apenas uma classe (tanto faz seja abstrata ou concreta), com interfaces não há limites, você pode implementar quantas quiser numa classe.
Imagine a seguinte modelagem:
abstract class Veiculo
{
private bool ligado = false;
public void Ligar()
{
this.ligado = true;
}
public abstract void Virar();
}
class Carro: Veiculo
{
public override void Virar()
{
// Implementa virar com Volante
}
}
class Moto: Veiculo
{
public override void Virar()
{
// Implementa virar com Guidão
}
}
No modelo acima, perceba que há uma relação de é um, uma relação de herança: Carro É UM Veiculo , Moto É UM Veiculo
Ambos são possíveis de ser ligados e a implementação á mesma através do método Ligar() da classe abstrata (isso não é possível com interfaces), entretanto, cada veiculo vira de uma forma diferente então essa implementação deve ser feita nas classes concretas.
Quando falamos de interfaces temos uma relação de tem um, ou seja, adicionamos um comportamento ou atributos que não são exclusivos da própria classe, usando o exemplo acima, vamos adicionar um comportamento de Janelas, através da interface:
public interface IJanela
{
int QuantidadeJanelas {get; set;}
void FecharJanelas();
void AbrirJanelas();
}
Perceba que quantidade de janelas, abrir e fechar podem se aplicados a qualquer outra coisa e não exclusivamente à um veículo. Uma casa, um barco, um prédio também utilizar essa interface de Janelas
IJanela é um comportamento que pode ser aplicado à veículos mas não a todos os veículos, por exemplo, motos não têm janelas! Portanto não faz o menor sentido ter essa interface implementada na classe Moto, então implementamos a interface somente na classe Carro, essa já uma questão que não dá pra fazer com classe abstratas, se veículos tivesse essa característica de janelas, a classe Moto seria obrigada a implementar mesmo não fazendo o menor sentido.
class Carro: Veiculo, IJanela
{
public override void Virar()
{
// Implementa virar com Volante
}
public int QuantidadeJanelas { get; set;}
public void FecharJanelas()
{
// fecha janelas
}
public void AbrirJanelas()
{
// abre janelas
}
}
Bom, resumidamente a questão é mais conceitual em relação a Programação Orientada a Objetos, lembrando que você é livre de não seguir os conceitos e criar uma interface que representa um relação é um, só se lembre que estará ferindo alguns princípios.
Sobre os modificadores de acesso, as implementações de interface devem sempre ser públicas, porque para a classe ter o comportamento da interface, esse comportamento deve ser explicito e acessível para que sejam encapsulados de maneira genérica:
IJanela objetoComJanela = new Carro();
objetoComJanela.AbrirJanelas();
objetoComJanela = new Casa();
objetoComJanela.AbrirJanelas();
No exemplo acima, se o método AbrirJanelas() não for público nem Carro nem Casa seguem a implementação de IJanela.
Quando o método AbrirJanelas() é chamado, não é chamado o método da interface e sim o método implementado na classe Carro ou Casa;
Não sei se ficou claro, basicamente é isso.
Referências para aprofundar:
Interfaces
POO
Bons estudos!