2009-01-07 13 views
61

Kicking wokół niektórych małych struktur odpowiadając this post, natknąłem się na następujący niespodziewanie:Automatyczne właściwości i struktury nie mieszają się?

następującej strukturze, przy użyciu pola int jest doskonale prawną:

struct MyStruct 
{ 
    public MyStruct (int size) 
    { 
     this.Size = size; // <-- Legal assignment. 
    } 

    public int Size; 
} 

jednak następującą strukturę, stosując automatyczne właściwość nie kompiluje się:

struct MyStruct 
{ 
    public MyStruct (int size) 
    { 
     this.Size = size; // <-- Compile-Time Error! 
    } 

    public int Size{get; set;} 
} 

Zwrócony błąd to "Ten" obiekt nie może być użyty, zanim wszystkie jego pola zostaną przypisane do ". Wiem, że jest to standardowa procedura dla struktury: pole zaplecza dla każdej właściwości musi zostać przypisane bezpośrednio (a nie za pośrednictwem zestawu właściwości zestawu właściwości) z poziomu konstruktora struktury.

Rozwiązaniem jest użycie wyraźne pole Podłoże:

struct MyStruct 
{ 
    public MyStruct(int size) 
    { 
     _size = size; 
    } 

    private int _size; 

    public int Size 
    { 
     get { return _size; } 
     set { _size = value; } 
    } 
} 

(Zauważ, że VB.NET nie miałby tego problemu, ponieważ w VB.NET wszystkie pola są automatycznie inicjowane 0/null/false gdy po raz pierwszy utworzony.)

To byłoby niefortunne ograniczenie podczas korzystania z właściwości automatycznych z structs w C#. Myśląc koncepcyjnie, zastanawiałem się, czy nie byłoby to rozsądne miejsce na wyjątek, który pozwala wywołać właściwość set accessor w ramach konstruktora struktury, przynajmniej dla właściwości automatycznej?

Jest to niewielki problem, prawie krawędzi przypadek, ale zastanawiałem się, co inni myśleli o tym ...

+2

Pola w języku C# są również inicjowane na wartość 0/null/false. Pamiętaj, że działa to w środowisku wykonawczym, a nie w określonym języku. ;) –

+0

Nie dla pól konstrukcji w C#. W przypadku struct, pola muszą być inicjowane przez jawny konstruktor lub przez wywołującego, jeśli używa się niejawnego konstruktora bez parametrów. VB.NET nie ma tego ograniczenia i dlatego powyższy przykład, który nie będzie kompilowany w C#, będzie kompilował i działał poprawnie w VB.NET. –

+0

możliwy duplikat [Dlaczego konieczne jest wywołanie: this() na struct, aby użyć właściwości automatycznych w języku C#? (Http://stackoverflow.com/questions/272153/why-is-it-necessary-to-call -to-na-strukturze-używać-automatycznych-w-c) – nawfal

Odpowiedz

80

Od C# 6 dalsza: nie jest to już problem


Becore C# 6, trzeba wywołać konstruktora domyślnego dla tej pracy:

public MyStruct(int size) : this() 
{ 
    Size = size; 
} 

większym problemem jest to, że masz zmienny struct. To jest nigdy dobry pomysł. Chciałbym zrobić to:

public int Size { get; private set; } 

Nie technicznie niezmienne, ale wystarczająco blisko.

Z ostatnich wersjach C#, można poprawić na to:

public int Size { get; } 

Można teraz tylko być przypisana w konstruktorze.

+0

Dzięki Marc, to ma sens, dziękuję, zapomniałem, że możemy wymusić domyślną inicjalizację. Zgadzam się, nigdy nie utworzyłbym struktury, którą można zmienić, ale odpowiadałem na czyjąś odpowiedź: here. –

+4

Myślę, że mówienie * nigdy * jest trochę mocne. Należy pozostawić niezmienność, ale sprawienie, by brzmiało jak mandat "nigdy nie jest dobrym pomysłem". ;) –

+6

Używanie absolutu to ** never ** a good idea ;-p (zobacz co tam zrobiłem ...). W rzeczy samej, * jeśli rozumiesz implikacje *, przydatne są zmienne struktury. Problem polega na tym, że większość ludzi ich nie rozumie i kosztuje ich dużo czasu i frustracji, których uczą się. –

10

Można rozwiązać ten problem przez pierwsze wywołanie konstruktora domyślnego:

struct MyStruct 
{ 
    public MyStruct(int size) : this() 
    { 
     this.Size = size; // <-- now works 
    } 

    public int Size { get; set; } 
} 
7

Innym niejasne obejście tego problemu jest jeden zauważony w tymczasowym Tuple klasy w Managed Extensibility Framework (przez Krzysztof Koźmic):

public struct TempTuple<TFirst, TSecond> 
{ 
    public TempTuple(TFirst first, TSecond second) 
    { 
     this = new TempTuple<TFirst, TSecond>(); // Kung fu! 
     this.First = first; 
     this.Second = second; 
    } 

    public TFirst First { get; private set; } 
    public TSecond Second { get; private set; } 

(Pełny kod źródłowy z Codeplex: Tuple.cs)

ja też pamiętać, że doku tacji dla CS0188 został zaktualizowany, aby dodać:

Jeśli widzisz ten błąd podczas próby zainicjować właściwość w struct konstruktora, rozwiązaniem jest zmiana parametr konstruktora określić pole podkładową zamiast sama właściwość . Auto-implementowane właściwości należy unikać w strukturach , ponieważ nie mają pola pomocniczego i dlatego nie mogą być zainicjowane w żaden sposób z konstruktora .

Więc biorę to oznaczać, że oficjalne wytyczne jest wykorzystanie właściwości starym stylu w swoich strukturach po uruchomieniu do tego problemu, który jest prawdopodobnie mniej niejasne (i więcej readible) niż którykolwiek z pozostałych dwie dotychczas zbadane alternatywy.

+0

Dziękujemy za aktualizację raportu o błędzie. Rzeczywiście, najbardziej wydajną sytuacją dla struktur jest użycie jawnego pola zaplecza. Jeśli ktoś chce zainicjować coś innego niż 0/null/false, to wymaga * dwóch * kroków, jeśli nie ma pola zaplecza: null out, a następnie ustaw aktualną wartość. –

+3

Linia "knung fu" jest naprawdę fajna, muszę przyznać. (Nie można tego zrobić z klasą.) Ale to jest to samo, co o deklarowaniu konstruktora "public TempTuple (najpierw TFirst, drugi TSecond): this()", co, jak sądzę, jest czystszym sposobem na zrobienie tego . (I wciąż kończymy inicjowanie każdego pola * dwa razy *.) –

Powiązane problemy