2010-04-30 12 views
5

Poniższy kod nie kompiluje się, powodując błąd "Widget musi być typem nie abstrakcyjnym z publicznym konstruktorem bez parametrów". Myślę, że kompilator ma wszystkie potrzebne informacje. Czy to błąd? Niedopatrzenie? A może jest jakiś scenariusz, w którym nie byłoby to ważne?Dlaczego klasyczne ograniczenie new() nie jest spełnione przez klasę z opcjonalnymi parametrami w konstruktorze?

public class Factory<T> where T : new() 
{ 
    public T Build() 
    { 
     return new T(); 
    } 
} 

public class Widget 
{ 
    public Widget(string name = "foo") 
    { 
     Name = name; 
    } 

    public string Name { get; set; } 
} 

public class Program 
{ 
    public static void Main() 
    { 
     var widget = new Widget(); // this is valid 
     var factory = new Factory<Widget>(); // compiler error 
    } 
} 
+0

Paging Eric Lippert .... Myślę, że będzie to w dół do opcjonalnych parametrów będących funkcją kompilatora, ale ogólne ograniczenia są CLR. Tak więc opcjonalne parametry są zastępowane przez kompilator, a JIT właśnie widzi (wymagane) parametry. – Richard

+0

@Richard: Zasadniczo jest to problem tutaj. Opcjonalne parametry mają również dziwne efekty uboczne dla wersji, z tego właśnie powodu ... –

Odpowiedz

7

Podczas gdy to logicznie powinno działać, niestety tak nie jest. Środowisko CLR nadal widzi konstruktora jako konstruktor oparty na parametrach.

Należy pamiętać, że podczas gdy C# obsługuje opcjonalne parametry, jest to wykonywane na poziomie kompilatora podczas kompilacji. Typ podstawowy nadal zawiera tylko konstruktor, który przyjmuje jeden parametr. Jeśli chodzi o CLR obawia się, że „domyślne parametry” są konwertowane do atrybutów, tak jak poniżej:

public Widget(([Optional, DefaultParameterValue("foo")] string name) { // ... 

CLR jest czas pracy w wielu językach. Generics są przygotowane do pracy na poziomie CLR, dla wszystkich języków, więc ograniczenia muszą być prawdziwe także w językach bez parametrów domyślnych. Języki nie są wymagane, aby zrozumieć atrybut OptionalAttribute ani DefaultParameterValueAttribute, więc nie może on działać jednakowo dla wszystkich języków, dlatego jest niedozwolony.


Edit:

W odpowiedzi na Twój komentarz:

Co ja nie rozumiem, dlaczego kompilator C# nie można wygenerować kod niezbędny do zaspokojenia CLR

Teoretycznie, zespół kompilatorów C# mógłby wygenerować dwa oddzielne konstruktory zamiast jednego konstruktora oznaczonego atrybutami. Mogłoby to potencjalnie rozbić na wiele konstruktorów, ponieważ nazwane parametry tworzą możliwości dla wielu, wielu możliwych kombinacji "konstruktorów" (lub metod wywoływania metod), szczególnie gdy dostępnych jest wiele argumentów. Osobiście cieszę się, że tego nie zrobili, ponieważ spowodowałoby to zamieszanie z powodu nadmiaru metod i konstruktorów generowanych typów, co spowodowałoby, że publiczny interfejs API wyglądałby zupełnie inaczej niż kod, który je wygenerował. Podjąć następujące konstruktora:

public Widget(
      int id = 0, 
      string name = "foo", 
      float width=1.0f, 
      float height=1.0f, 
      float depth=1.0f 
     ) { // ... 

Byłaś do automatycznego generowania wszystkich możliwych kombinacjach tu kompilator musiałby generować konstruktorów dla tego jednego konstruktora, ponieważ istnieje N! możliwe sposoby na wywołanie tego ...

+0

@Joshua: Kompilator C# generuje kod, który wkleiłem powyżej. Tak to działa - nie tworzy konstruktora bez parametrów, ale raczej opcjonalnego. Będę edytować, aby dodać szczegóły ... –

+0

OK, dziękuję, to wyjaśnia, dlaczego to nie działa. Nadal zastanawiam się, jakie byłyby konsekwencje generowania konstruktora bez parametrów. –

+0

@Joshua: Edytowałem swoją odpowiedź, aby wyjaśnić, dlaczego jest to potencjalnie zły pomysł ... Czy masz teraz większy sens? Pamiętaj, że to nie tylko opcjonalne, możesz także użyć nazwanych parametrów, więc N parametrów oznacza N! możliwe kombinacje, które należałoby wygenerować ... –

Powiązane problemy