Czy mogę powiedzieć C # zerowalne odwołania, że ​​metoda jest faktycznie kontrola zerowa w polu

14

Rozważ następujący kod:

#nullable enable
class Foo
{
    public string? Name { get; set; }
    public bool HasName => Name != null;
    public void NameToUpperCase()
    {
        if (HasName)
        {
            Name = Name.ToUpper();
        }
    }
}

Na Name = Name.ToUpper () pojawia się ostrzeżenie, że Name to możliwe odwołanie zerowe, co jest wyraźnie niepoprawne. Mogę wyleczyć to ostrzeżenie, wpisując HasName, więc warunkiem jest if (Nazwa! = Null).

Czy jest jakiś sposób, w jaki mogę pouczyć kompilator, że prawdziwa odpowiedź HasName implikuje ograniczenie związane z brakiem wartości dopuszczalnej dla Name?

Jest to ważne, ponieważ HasName może faktycznie przetestować o wiele więcej rzeczy i może chciałbym użyć go w kilku miejscach lub może to być publiczna część powierzchni API. Istnieje wiele powodów, dla których warto uwzględnić sprawdzanie wartości NULL we własnej metodzie, ale wydaje się, że powoduje to uszkodzenie sprawdzania wartości referencyjnych.

John Melville
źródło
1
IMO powinieneś używać HasValuena typ zerowalny, a nie sprawdzać go null. Prawdopodobnie nie wpływa to jednak na twój problem.
fredrik
Myślę, że w twoim przypadku możesz owinąć kod #nullable disablenastępnie #nullable enablelub restorepóźniej ( docs.microsoft.com/en-us/dotnet/csharp/… ).
GaryNg
5
możesz użyć !operatora „cholera” . if(HasName) { Name = Name!.ToUpper(); }
Jan Paolo Idź
1
w przypadku aplikacji wielowątkowej nazwa może mieć wartość NULL po sprawdzeniu HasName, używanie zmiennej lokalnie zamiast wracania do właściwości (kto wie, co właściwość może zrobić w module pobierającym), spowoduje błędy funkcyjne (pamiętaj, że użycie procedury obsługi zdarzeń, gdy tak się stało)
XIU

Odpowiedzi:

3

Rozejrzałem się po różnych atrybutach System.Diagnostics.CodeAnalysisi nie mogłem znaleźć niczego odpowiedniego, co jest bardzo rozczarowujące. Najbliżej tego, co możesz osiągnąć, wydaje się być:

public bool TryGetName([NotNullWhen(true)] out string? name)
{
    name = Name;
    return name != null;
}

public void NameToUpperCase()
{
    if (TryGetName(out var name))
    {
        Name = name.ToUpper();
    }
}

Wiem, wygląda to dość nieporęcznie. Możesz zajrzeć do dokumentów MSDN pod kątem atrybutów zerowalnych , może znajdziesz coś fajniejszego.

V0ldek
źródło
2
Wydaje się, że potrzebujemy więcej atrybutów lub coś w rodzaju twierdzeń maszynopisu
Stilgar
Jako odpowiedź wybiorę tę, ponieważ wydaje się, że prawdziwą odpowiedzią, jak się obawiałem, jest „nie, c # jeszcze tego nie robi”.
John Melville
@JohnMelville Nie byłem w stanie znaleźć propozycji takiej funkcji, więc nie sądzę, że możemy się spodziewać tej zmiany w najbliższym czasie.
V0ldek
2
@XIU Kompilator jest już luźny w tym aspekcie. Jeśli to zrobisz if(Name != null) return Null.ToUpper(), nie będzie ostrzeżenia o zerowym wyłuskaniu, nawet jeśli technicznie jest to warunek wyścigu w TOCTOU. Pamiętam, jak Mads Torgersen mówił o tym, jak to uważali, ale generowałoby tak wiele fałszywych trafień, że cała nullowa typy referencji byłaby bezużyteczna - 99% czasu twoje właściwości nie zostaną zmienione przez inny wątek. Wszystko, co musisz zrobić, to utworzyć atrybut, który sprawi, że sprawdzenie tej właściwości będzie traktowane jako sprawdzenie wartości null w innej właściwości.
V0ldek
2
Naprawiłem problem „nie mogę znaleźć propozycji tego”. ( github.com/dotnet/csharplang/issues/2997 ) Życz mi powodzenia.
John Melville
-10

String jest typem odniesienia, a nullable (np. int?) To typy wartości nullable. Więc tak naprawdę nie możesz tego zrobić string? myString; Potrzebujesz tego:

class Foo
{
    public string Name { get; set; }
    public bool HasName => !String.IsNullOrEmpty(Name);  ////assume you want empty to be treated same way as null
    public void NameToUpperCase()
    {
        if (HasName)
        {
            Name = Name.ToUpper();
        }
    }
}
daxu
źródło