Dlaczego parametry opcjonalne C # 4 zdefiniowane w interfejsie nie są wymuszane przy implementacji klasy?

361

Zauważyłem, że przy opcjonalnych parametrach w C # 4, jeśli podasz opcjonalny parametr w interfejsie, którego nie używasz , musisz uczynić ten parametr opcjonalnym dla dowolnej klasy implementującej:

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

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

i dlatego:

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

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

Czy ktoś wie, dlaczego parametry opcjonalne zostały zaprojektowane w taki sposób?

Z jednej strony przypuszczam, że możliwość przesłonięcia wartości domyślnych określonych w interfejsach jest przydatna, ale szczerze mówiąc, nie jestem pewien, czy powinieneś być w stanie określić wartości domyślne w interfejsie, ponieważ powinna to być decyzja implementacyjna.

Z drugiej strony to rozłączenie oznacza, że ​​nie zawsze można używać zamiennie konkretnej klasy i interfejsu. To oczywiście nie stanowiłoby problemu, jeśli wartość domyślna jest podana w implementacji, ale jeśli ujawniasz swoją konkretną klasę jako interfejs (używając na przykład niektórych struktur IOC do wstrzyknięcia konkretnej klasy), to naprawdę nie ma punkt o wartości domyślnej, ponieważ dzwoniący zawsze musi ją podać.

theburningmonk
źródło
22
Ponieważ są opcjonalne?
Oded
1
Ale można rzutować obiektu do instancji MyInterfacei wywołać ją z opcjonalnego parametru: ((MyInterface)obj).TestMethod();.
Jim Mischel,
7
@oded - ale jeśli powiesz, że ten parametr jest opcjonalny w umowie, dlaczego pozwalasz implementatorowi, aby nie był opcjonalny? czy to nie powoduje zamieszania dla każdego, kto chce skorzystać z umowy?
theburningmonk
1
Myślę, że w tym przypadku można powiedzieć, że parametr jest opcjonalny w implementacji, a nie w wywoływaniu metod implementacji. Kiedy wywołujesz metodę w klasie, musisz przestrzegać reguł klasy (parametr nie jest opcjonalny w klasie, więc możesz wywołuje metodę bez niej), a po drugiej stronie, kiedy implementujesz interfejs, musisz przestrzegać reguł interfejsu, abyś mógł zastąpić metody z / bez parametrów opcjonalnych. Po prostu opinia.
Mohamad Alhamoud,
2
Bardziej szczegółowe wyjaśnienie problemu tutaj -> geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/…
Andrew Orsich

Odpowiedzi:

235

AKTUALIZACJA: To pytanie było przedmiotem mojego bloga w dniu 12 maja 2011 r. Dzięki za świetne pytanie!

Załóżmy, że masz opisany interfejs i sto klas, które go implementują. Następnie zdecydujesz, aby jeden z parametrów jednej z metod interfejsu był opcjonalny. Czy sugerujesz, że właściwą rzeczą jest, aby kompilator zmusił programistę do znalezienia każdej implementacji tej metody interfejsu i uczynił parametr również opcjonalnym?

Załóżmy, że to zrobiliśmy. Załóżmy teraz, że programista nie miał kodu źródłowego dla implementacji:


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

Jak autor D ma to zrobić? Czy są wymagane w twoim świecie, aby zadzwonić do autora B przez telefon i poprosić go o dostarczenie nowej wersji B, która sprawia, że ​​metoda ma opcjonalny parametr?

To nie będzie latać. Co jeśli dwie osoby wywołują autora B, a jedna z nich chce, aby domyślna była prawdziwa, a jedna z nich chce, aby była fałszywa? Co jeśli autor B po prostu odmawia gry?

Być może w takim przypadku musieliby powiedzieć:

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

Proponowana funkcja wydaje się powodować wiele niedogodności dla programisty bez odpowiedniego wzrostu mocy reprezentatywnej. Jakie są zalety tej funkcji, które uzasadniają wzrost kosztów dla użytkownika?


AKTUALIZACJA: W poniższych komentarzach supercat sugeruje funkcję językową, która naprawdę dodałaby moc do języka i umożliwiła niektóre scenariusze podobne do opisanych w tym pytaniu. Do Twojej dyspozycji ta funkcja - domyślne implementacje metod w interfejsach - zostanie dodana do C # 8.

Eric Lippert
źródło
9
@ supercat: Nie planujemy tego, ale taka funkcja jest możliwa. Zostało to zaproponowane wcześniej. Podczas projektowania C # 4 omawialiśmy wiele funkcji, które nazywaliśmy „rozszerzaniem wszystkiego” - mamy metody rozszerzeń, więc co by to znaczyło, mieć rozszerzenia, właściwości rozszerzenia, konstruktory rozszerzeń, interfejsy rozszerzeń itd. . Klasy, które można „dołączyć” do interfejsów i powiedzieć „te metody są domyślnymi implementacjami tego interfejsu”, są jednym z możliwych sposobów scharakteryzowania „interfejsów rozszerzeń”. Ciekawy pomysł.
Eric Lippert,
16
wyjaśnienie mojego uzasadnienia, dlaczego uważam, że nie jest intuicyjne: dla mnie parametr opcjonalny jest „opcjonalny dla osoby wywołującej metodę” NIE ”opcjonalny dla implementatora interfejsu”.
Stefan de Kok
8
„Czy w twoim świecie są wymagane, aby zadzwonić do autora B przez telefon i poprosić go o przesłanie nowej wersji [...]?” Nie, żadne rozmowy telefoniczne nie są wymagane. B po prostu nie powinno być dłużej uważane za wszczepienie MyInterface, a autor D może zostać o tym poinformowany przez kompilator. Autor D byłby zobowiązany do implementacji D za pomocą TestMethod, który akceptuje parametr domyślny, ponieważ tego właśnie wymaga autor interfejsu - argumentujesz przeciwko zezwoleniu autorowi interfejsu na wymuszenie ograniczenia, ponieważ ktoś może chcieć je złamać. To nie jest dobry argument.
philofinfinitejest
7
@EricLippert W przypadku, gdy dla wygody kodowania określono parametr opcjonalny, tak, wymuszanie niedogodności we wdrażaniu jest sprzeczne z intuicją. Ale w przypadku, gdy opcjonalny parametr jest używany w celu zminimalizowania przeciążenia metody, jest to potężna cecha, te opcjonalne wartości mogą nabierać wielkiego znaczenia i ich ignorowanie z pewnością osłabia tę moc. Nie wspominając o przypadku, w którym konkretna implementacja określa inną wartość domyślną niż interfejs. Wydaje się to bardzo dziwnym rozłączeniem.
philofinfinitejest
8
Zgadzam się z @philofinfinitejest tutaj. Interfejs mówi, co można zrobić. Jeśli otrzymałem implementację interfejsu o innej wartości domyślnej niż określona przez interfejs, skąd mam to wiedzieć? Hej, mam interfejs, który domyślnie przyjmuje wartość true, więc dlaczego ta rzecz staje się fałszywa? Wydaje się, że NIE dostałem interfejsu, którego się spodziewałem. Co gorsza, muszę teraz zaprogramować implementację, a nie interfejs.
aaronburro
47

Opcjonalny parametr jest właśnie oznaczony atrybutem. Ten atrybut mówi kompilatorowi, aby wstawił wartość domyślną tego parametru w miejscu wywołania.

Wywołanie obj2.TestMethod();zostaje zastąpione przez obj2.TestMethod(false);kompilację kodu C # do IL, a nie w czasie JIT.

Tak więc w pewnym sensie zawsze dzwoniący podaje wartość domyślną z opcjonalnymi parametrami. Ma to również konsekwencje dla wersji binarnej: jeśli zmienisz wartość domyślną, ale nie przekompilujesz kodu wywołującego, nadal będzie używać starej wartości domyślnej.

Z drugiej strony to rozłączenie oznacza, że ​​nie zawsze można używać zamiennie konkretnej klasy i interfejsu.

Nie możesz tego zrobić, jeśli metoda interfejsu została zaimplementowana jawnie .

CodesInChaos
źródło
1
Czy możesz jednak zmusić implementatorów do jawnego wdrożenia?
zmiażdżyć
30

Ponieważ parametry domyślne są rozwiązywane w czasie kompilacji, a nie w czasie wykonywania. Zatem wartości domyślne nie należą do wywoływanego obiektu, ale do typu odwołania, przez który jest on wywoływany.

Olhovsky
źródło
7

Z tego, co rozumiem, parametry opcjonalne są jak podstawienie makr. Nie są tak naprawdę opcjonalne z punktu widzenia metody. Artefaktem tego jest zachowanie, które widać, gdy uzyskuje się różne wyniki po rzutowaniu na interfejs.

Ariel Arjona
źródło