Perché i parametri opzionali C # 4 definiti sull'interfaccia non vengono applicati sulla classe di implementazione?


361

Ho notato che con i parametri opzionali in C # 4 se si specifica un parametro facoltativo su un'interfaccia che non si desidera , è necessario rendere quel parametro facoltativo su qualsiasi classe di implementazione:

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

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

e quindi:

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

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

Qualcuno sa perché i parametri opzionali sono progettati per funzionare in questo modo?

Da un lato suppongo che la capacità di sovrascrivere i valori predefiniti specificati sulle interfacce sia utile anche se ad essere onesti non sono sicuro che dovresti essere in grado di specificare i valori predefiniti sull'interfaccia in quanto dovrebbe essere una decisione di implementazione.

D'altra parte, questa disconnessione significa che non è sempre possibile utilizzare la classe concreta e l'interfaccia in modo intercambiabile. Questo, naturalmente, non sarebbe un problema se il valore predefinito è specificato sull'implementazione, ma quindi se stai esponendo la tua classe concreta come interfaccia (usando un framework IOC per iniettare la classe concreta per esempio) allora davvero non c'è punto avendo il valore predefinito come il chiamante dovrà sempre fornirlo comunque.


22
Perché sono opzionali?
Oded

1
Ma si può lanciare l'istanza dell'oggetto per MyInterfacee chiamarlo con il parametro opzionale: ((MyInterface)obj).TestMethod();.
Jim Mischel,

7
@oded - ma se si dice che questo parametro è facoltativo nel contratto, perché si consente all'implementatore di non renderlo facoltativo? ciò non causa solo confusione a chiunque cerchi di utilizzare il contratto?
theburningmonk,

1
Penso che in questo caso puoi dire che il parametro è facoltativo nell'implementazione, non nel chiamare i metodi di implementazione. Quando chiami il metodo nella classe devi seguire le regole della classe (il parametro non è facoltativo nella classe in modo da poter chiamare il metodo senza di esso), e nella seconda mano quando si implementa l'interfaccia è necessario seguire le regole dell'interfaccia, in modo da poter sovrascrivere i metodi con / senza parametri opzionali. Solo un'opinione.
Mohamad Alhamoud,

2
Maggiori spiegazioni dettagliate sui problemi qui -> geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/…
Andrew Orsich

Risposte:


236

AGGIORNAMENTO: Questa domanda è stata l'oggetto del mio blog il 12 maggio 2011. Grazie per l'ottima domanda!

Supponi di avere un'interfaccia come descrivi e un centinaio di classi che la implementano. Quindi si decide di rendere facoltativo uno dei parametri di uno dei metodi dell'interfaccia. Stai suggerendo che la cosa giusta da fare è che il compilatore costringa lo sviluppatore a trovare tutte le implementazioni di quel metodo di interfaccia e rendere facoltativo anche il parametro?

Supponiamo di averlo fatto. Supponiamo ora che lo sviluppatore non avesse il codice sorgente per l'implementazione:


// 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

Come dovrebbe fare l'autore di D per farlo funzionare? Sono obbligati nel tuo mondo a chiamare l'autore di B al telefono e chiedere loro di inviare loro una nuova versione di B che rende il metodo un parametro opzionale?

Non volerà. Cosa succede se due persone chiamano l'autore di B e uno di loro vuole che il valore predefinito sia vero e uno di loro vuole che sia falso? E se l'autore di B si rifiuta semplicemente di suonare insieme?

Forse in quel caso sarebbero tenuti a dire:

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

La funzionalità proposta sembra aggiungere molti inconvenienti al programmatore senza un corrispondente aumento del potere rappresentativo. Qual è il vantaggio convincente di questa funzione che giustifica l'aumento dei costi per l'utente?


AGGIORNAMENTO: Nei commenti seguenti, supercat suggerisce una funzionalità linguistica che aggiungerebbe realmente potere alla lingua e consentirebbe alcuni scenari simili a quello descritto in questa domanda. Cordiali saluti, quella caratteristica - implementazioni predefinite di metodi nelle interfacce - verrà aggiunta a C # 8.


9
@supercat: Non abbiamo in programma di farlo, ma una funzionalità del genere è possibile. È stato proposto prima. Durante il processo di progettazione per C # 4 abbiamo dato il via a molte delle cosiddette funzionalità "estensione tutto": abbiamo metodi di estensione, quindi cosa significherebbe avere eventi di estensione, proprietà di estensione, costruttori di estensione, interfacce di estensione e così via . Le classi che è possibile "collegare" alle interfacce e dire "questi metodi sono le implementazioni predefinite di questa interfaccia" sono un modo possibile per caratterizzare le "interfacce di estensione". Un'idea interessante
Eric Lippert,

16
chiarire il mio ragionamento sul perché ritengo che non sia intuitivo: per me un parametro facoltativo è "facoltativo per il chiamante del metodo" NON "facoltativo per l'implementatore dell'interfaccia".
Stefan de Kok,

8
"Sono tenuti nel tuo mondo a chiamare l'autore di B al telefono e chiedere loro di spedire per favore una nuova versione [...]?" No, non sono necessarie chiamate telefoniche. B non dovrebbe semplicemente essere più in grado di essere considerato un impianto di MyInterface e l'autore di D potrebbe esserne informato dal compilatore. L'autore di D dovrebbe implementare D con un TestMethod che accetta un parametro predefinito poiché questo è ciò che richiede l'autore dell'interfaccia: stai discutendo di non consentire all'autore dell'interfaccia di applicare un vincolo perché qualcuno potrebbe voler romperlo. Questo non è un buon argomento.
philofinfinitejest,

7
@EricLippert Nel caso in cui si specifichi il parametro facoltativo per comodità di codifica, sì, applicare un inconveniente nell'implementazione è controintuitivo. Ma nel caso in cui il parametro opzionale venga utilizzato per ridurre al minimo il sovraccarico del metodo, una caratteristica potente, quei valori opzionali possono assumere un grande significato e ignorarli certamente diluisce quella potenza. Per non parlare del caso in cui l'implementazione concreta specifica un valore predefinito diverso dall'interfaccia. Sembra solo una disconnessione molto strana per consentire.
philofinfinitejest,

8
Sono d'accordo con @philofinfinitejest qui. Un'interfaccia dice cosa può fare qualcosa. Se ho l'implementazione di un'interfaccia con un valore predefinito diverso da quello specificato dall'interfaccia, come faccio a saperlo? Ehi, ho un'interfaccia che passa true come impostazione predefinita, quindi perché questa cosa sta diventando falsa? Quello, sembrerebbe come se non avessi l'interfaccia che mi aspettavo. Ancora peggio, ora devo programmare una implementazione e non un'interfaccia.
aaronburro,

47

Un parametro facoltativo è appena taggato con un attributo. Questo attributo indica al compilatore di inserire il valore predefinito per quel parametro nel sito di chiamata.

La chiamata obj2.TestMethod();viene sostituita da obj2.TestMethod(false);quando il codice C # viene compilato in IL e non in JIT-time.

Quindi in un certo senso è sempre il chiamante a fornire il valore predefinito con parametri opzionali. Ciò ha conseguenze anche sul controllo delle versioni binario: se si modifica il valore predefinito ma non si ricompila il codice chiamante, continuerà a utilizzare il vecchio valore predefinito.

D'altra parte, questa disconnessione significa che non è sempre possibile utilizzare la classe concreta e l'interfaccia in modo intercambiabile.

Non puoi già farlo se il metodo di interfaccia è stato implementato esplicitamente .


1
Puoi forzare gli implementatori a implementare esplicitamente però?
schiaccia

30

Poiché i parametri predefiniti vengono risolti in fase di compilazione, non in fase di esecuzione. Quindi i valori predefiniti non appartengono all'oggetto chiamato, ma al tipo di riferimento attraverso il quale viene chiamato.


7

I parametri opzionali sono un po 'come una sostituzione macro da quello che ho capito. Non sono realmente opzionali dal punto di vista del metodo. Un artefatto di ciò è il comportamento che vedi dove ottieni risultati diversi se esegui il cast su un'interfaccia.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.