Błąd CS0029: co dokładnie mówi kompilator
Kompilator nie odmawia dlatego, że kod jest zły. Ten komunikat znaczy, że między dwoma typami nie ma żadnej drogi, a nie że brakuje rzutowania.
CS0029 znaczy, że między typem, który podałeś, a typem, którego oczekuje zmienna, nie istnieje żadna konwersja. Nie brakuje rzutowania. Kompilator nie zna sposobu zamiany jednego typu na drugi i nie zamierza go wymyślać za Ciebie.
Pełny komunikat z prawdziwej kompilacji wygląda tak:
Program.cs(2,12): error CS0029: Nie można niejawnie przekonwertować typu „string” na „int”.
Dwie liczby w nawiasie to linia i kolumna. Kolumna 12 wskazuje
Console.ReadLine(), a nie deklarację zmiennej, i to jest pierwsza wskazówka:
Console.Write("Podaj swój wiek: ");
int wiek = Console.ReadLine();
Console.ReadLine() zwraca string. Zawsze, również wtedy, gdy użytkownik
wpisze 20. Zmienna wiek jest typu int, więc kompilator ma po jednej stronie
tekst, a po drugiej liczbę całkowitą, i żadna reguła języka nie mówi mu, co
zrobić z wejściem „dwadzieścia” albo z samym Enterem. Zamiast wybierać za
Ciebie, przerywa kompilację.
Poprawka nazywa operację wprost:
int wiek = int.Parse(Console.ReadLine());
int.Parse zgłasza FormatException, gdy tekst nie jest liczbą, więc w
programie, który czyta cokolwiek od użytkownika, sensowniejszy jest
int.TryParse:
if (int.TryParse(Console.ReadLine(), out int wiek))
{
Console.WriteLine($"Za rok będziesz mieć {wiek + 1} lat.");
}
else
{
Console.WriteLine("To nie jest liczba.");
}
Wpisane dwadzieścia daje wtedy To nie jest liczba. zamiast przerwanego
programu.
Sam kompilator konwertuje niejawnie tylko wtedy, gdy nie da się przy tym nic
stracić. int jest konwertowany na long i na double bez pytania, bo każda
wartość int mieści się w obu. Dotyczy to nawet char na int:
char litera = 'K';
int kodZnaku = litera;
Kompiluje się i wypisuje 75, czyli kod znaku K. W drugą stronę, z int na
char, kompilator wymaga już rzutowania, bo większość wartości int nie
odpowiada żadnemu znakowi. Tekst i liczba nie mieszczą się w tej regule w żadną
stronę, i stąd CS0029.
Dwa kolejne warianty: char na string i int na bool
Drugi pojawia się przy pierwszym sięgnięciu po znak z tekstu:
Program.cs(2,25): error CS0029: Nie można niejawnie przekonwertować typu „char” na „string”.
slowo[0] zwraca char, czyli pojedynczy znak, a nie tekst o długości jeden. C#
nie traktuje jednego jako skróconej wersji drugiego, więc potrzebne jest
slowo[0].ToString().
Trzeci wariant nie jest problemem z typami, tylko literówką, i dostajesz za niego taki komunikat:
Program.cs(4,5): error CS0029: Nie można niejawnie przekonwertować typu „int” na „bool”.
Winna jest jedna linia:
if (wiek = 18)
Jeden znak równości przypisuje, dwa porównują. Wyrażenie wiek = 18 ma wartość
18 i typ int, a if wymaga bool. W C, gdzie zero znaczy fałsz, taki kod
skompilowałby się i cicho robił coś innego, niż zaplanowałeś. Ten sam upór
kompilatora, który przy Console.ReadLine() wygląda na przeszkodę, tutaj
wychwytuje błąd, którego sam byś nie znalazł.
Przypisania nie są przy tym jedynym miejscem. Ten sam numer wychodzi z instrukcji
return, gdy metoda deklaruje int, a zwraca tekst:
Program.cs(5,12): error CS0029: Nie można niejawnie przekonwertować typu „string” na „int”.
Kolumna wskazuje wtedy wyrażenie po return, nie nagłówek metody.
CS0266 wygląda podobnie i wymaga czegoś innego
Jeśli w komunikacie pojawia się zdanie o konwersji jawnej, to nie ten błąd:
Program.cs(4,19): error CS0266: Nie można niejawnie przekonwertować typu „double” na „int”. Istnieje konwersja jawna (czy nie brakuje rzutu?).
Tu konwersja istnieje i kompilator ją zna. Nie wykona jej sam, bo (int) obcina
część dziesiętną, a na utratę danych trzeba się zgodzić wprost. Rzutowanie
(int)srednia jest tu całą poprawką.
Podział nie zależy od tego, czy typy są spokrewnione. object na string również
daje CS0266, bo rzutowanie ma sens, nawet jeśli w czasie działania programu może
się nie udać.
Przy CS0029 nie ma czego rzutować, więc ta sama próba kończy się kolejnym błędem:
Program.cs(1,12): error CS0030: Nie można przekonwertować typu „string” na „int”.
Dlatego rada „dodaj rzutowanie”, która powtarza się pod większością pytań o konwersję typów w C#, przy tym błędzie prowadzi w ślepą uliczkę. Najpierw sprawdź, czy komunikat wspomina o konwersji jawnej.
Komunikaty są po polsku, gdy język interfejsu systemu jest polski. Na angielskim
Windowsie ten sam błąd brzmi Cannot implicitly convert type 'string' to 'int'
i pod tą wersją znajdziesz więcej odpowiedzi.
W edytorze widać go jeszcze przed kompilacją, bo DevJourney uruchamia Roslyn Language Server, ten sam silnik, którego używa Visual Studio, i podkreśla obie strony przypisania od razu. Umiejętność przeczytania takiego komunikatu jest jedną z rzeczy, które realnie decydują o pierwszej pracy, a nie wybór języka.
Program kursu pokazuje, w jakiej kolejności układają się te podstawy.