Por que os parâmetros opcionais do C # 4 definidos na interface não são impostos na classe de implementação?

361

Notei que, com os parâmetros opcionais em C # 4, se você especificar um parâmetro opcional em uma interface , não precisará torná-lo opcional em qualquer classe de implementação:

public interface MyInterface
{
    void TestMethod(bool flag = false);
}

public class MyClass : MyInterface
{
    public void TestMethod(bool flag)
    {
        Console.WriteLine(flag);
    }
}

e portanto:

var obj = new MyClass();        
obj.TestMethod(); // compiler error

var obj2 = new MyClass() as MyInterface;
obj2.TestMethod(); // prints false

Alguém sabe por que os parâmetros opcionais são projetados para funcionar dessa maneira?

Por um lado, suponho que a capacidade de substituir qualquer valor padrão especificado nas interfaces seja útil, mas para ser sincero, não tenho certeza se você deve especificar valores padrão na interface, pois isso deve ser uma decisão de implementação.

Por outro lado, essa desconexão significa que você nem sempre pode usar a classe concreta e a interface de forma intercambiável. É claro que isso não seria um problema se o valor padrão for especificado na implementação, mas se você estiver expondo sua classe concreta como interface (usando alguma estrutura do IOC para injetar a classe concreta, por exemplo), então realmente não há ponto que possui o valor padrão, pois o chamador sempre deve fornecê-lo de qualquer maneira.

theburningmonk
fonte
22
Porque eles são opcionais?
Oded
11
Mas você pode converter a instância objeto para MyInterfacee chamá-lo com o parâmetro opcional: ((MyInterface)obj).TestMethod();.
Jim Mischel
7
@oded - mas se você diz que este parâmetro é opcional no contrato, por que você permite que o implementador não o torne opcional? isso não causa apenas confusão para quem quer usar o contrato?
theburningmonk
11
Penso que, neste caso, você pode dizer que o parâmetro é opcional na implementação, não na chamada dos métodos de implementação. Quando você chama o método na classe, deve seguir as regras da classe (o parâmetro não é opcional na classe para que você possa chame o método sem ele) e, na segunda mão, quando você implementa a interface, precisa seguir as regras da interface, para que você possa substituir os métodos com / sem parâmetros opcionais. Apenas uma opinião.
Mohamad Alhamoud
2
Mais detalhes problema explicação aqui -> geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/...
Andrew Orsich

Respostas:

236

ATUALIZAÇÃO: Esta questão foi o assunto do meu blog em 12 de maio de 2011. Obrigado pela ótima pergunta!

Suponha que você tenha uma interface descrita e uma centena de classes que a implementam. Então você decide tornar opcional um dos parâmetros de um dos métodos da interface. Você está sugerindo que a coisa certa a fazer é que o compilador force o desenvolvedor a encontrar todas as implementações desse método de interface e torne o parâmetro opcional também?

Suponha que fizemos isso. Agora, suponha que o desenvolvedor não tenha o código fonte para a implementação:


// in metadata:
public class B 
{ 
    public void TestMethod(bool b) {}
}

// in source code
interface MyInterface 
{ 
    void TestMethod(bool b = false); 
}
class D : B, MyInterface {}
// Legal because D's base class has a public method 
// that implements the interface method

Como o autor de D deve fazer esse trabalho? Eles são obrigados em seu mundo a ligar para o autor de B no telefone e pedir que eles enviem uma nova versão de B que faça com que o método tenha um parâmetro opcional?

Isso não vai voar. E se duas pessoas telefonarem para o autor de B e uma delas quiser que o padrão seja verdadeiro e uma delas queira que seja falso? E se o autor de B simplesmente se recusar a brincar?

Talvez nesse caso eles devessem dizer:

class D : B, MyInterface 
{
    public new void TestMethod(bool b = false)
    {
        base.TestMethod(b);
    }
}

O recurso proposto parece acrescentar muitos inconvenientes ao programador sem aumento correspondente no poder representativo. Qual é o benefício atraente desse recurso que justifica o aumento do custo para o usuário?


ATUALIZAÇÃO: nos comentários abaixo, o supercat sugere um recurso de idioma que realmente acrescentaria energia ao idioma e permitiria alguns cenários semelhantes ao descrito nesta pergunta. Para sua informação, esse recurso - implementações padrão de métodos em interfaces - será adicionado ao C # 8.

Eric Lippert
fonte
9
@ supercat: Não temos planos de fazê-lo, mas um recurso como esse é possível. Já foi proposto antes. Durante o processo de design do C # 4, analisamos muito do que chamamos de recursos de "extensão de tudo" - temos métodos de extensão, então o que significaria ter eventos de extensão, propriedades de extensão, construtores de extensão, interfaces de extensão etc. . As classes que você pode "anexar" às interfaces e dizer "esses métodos são as implementações padrão dessa interface" são uma maneira possível de caracterizar as "interfaces de extensão". Uma ideia interessante.
precisa
16
para esclarecer meu raciocínio sobre o motivo pelo qual acho que não é intuitivo: para mim, um parâmetro opcional é "opcional para o responsável pela chamada do método" NÃO "opcional para o implementador da interface".
Stefan de Kok
8
"Eles são obrigados em seu mundo a telefonar para o autor de B pelo telefone e pedir que eles lhes enviem uma nova versão [...]?" Não, nenhuma ligação é necessária. B simplesmente não deveria mais ser considerado uma implentação de MyInterface e o autor de D poderia ser informado disso pelo compilador. O autor de D seria obrigado a implementar D com um TestMethod que aceite um parâmetro padrão, pois é isso que o autor da interface exige - você está argumentando contra permitir que o autor da interface imponha uma restrição, porque alguém pode querer quebrá-la. Esse não é um bom argumento.
philofinfinitejest
7
@EricLippert No caso em que o parâmetro opcional está sendo especificado para conveniência de codificação, sim, impor inconveniência na implementação é contra-intuitivo. Porém, no caso em que o parâmetro opcional está sendo usado para minimizar a sobrecarga do método, um recurso poderoso, esses valores opcionais podem ter um grande significado e ignorá-los certamente dilui esse poder. Sem mencionar o caso em que a implementação concreta especifica um valor padrão diferente da interface. Parece apenas uma desconexão muito estranha para permitir.
philofinfinitejest
8
Eu concordo com @philofinfinitejest aqui. Uma interface informa o que algo pode fazer. Se eu receber uma implementação de uma interface com um valor padrão diferente do que a interface especifica, como devo saber isso? Ei, eu tenho uma interface que passa true como padrão, por que isso está ficando falso? Parece que eu NÃO recebi a interface que eu esperava. Pior ainda, agora tenho que programar para uma implementação e não para uma interface.
Aaronburro
47

Um parâmetro opcional é apenas marcado com um atributo. Este atributo diz ao compilador para inserir o valor padrão para esse parâmetro no site de chamada.

A chamada obj2.TestMethod();é substituída por obj2.TestMethod(false);quando o código C # é compilado para IL e não no momento do JIT.

De certa forma, é sempre o chamador que fornece o valor padrão com parâmetros opcionais. Isso também tem consequências no controle de versão binário: se você alterar o valor padrão, mas não recompilar o código de chamada, ele continuará usando o valor padrão antigo.

Por outro lado, essa desconexão significa que você nem sempre pode usar a classe concreta e a interface de forma intercambiável.

Você já não pode fazer isso se o método da interface foi implementado explicitamente .

CodesInChaos
fonte
11
Você pode forçar os implementadores a implementarem explicitamente?
esmagar
30

Como os parâmetros padrão são resolvidos no tempo de compilação, não no tempo de execução. Portanto, os valores padrão não pertencem ao objeto que está sendo chamado, mas ao tipo de referência pelo qual ele está sendo chamado.

Olhovsky
fonte
7

Parâmetros opcionais são como uma substituição de macro pelo que entendi. Eles não são realmente opcionais do ponto de vista do método. Um artefato disso é o comportamento que você vê onde obtém resultados diferentes se converter para uma interface.

Ariel Arjona
fonte