Was ist der ?[]? Syntax in C #?


85

Während ich den Delegierten studierte, in dem es sich eigentlich um eine abstrakte Klasse handelt Delegate.cs, sah ich die folgende Methode, die ich nicht verstehe

  • Warum der Rückgabewert verwendet wird, ?obwohl es sich bereits um einen Referenztyp ( Klassentyp ) handelt
  • ?[]? Bedeutung auf dem Parameter

Könntest du erklären?

public static Delegate? Combine(params Delegate?[]? delegates)
{
    if (delegates == null || delegates.Length == 0)
        return null;

    Delegate? d = delegates[0];
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}


23
Ist es nicht ein nullbares Array, das nullbare Werte enthalten kann?
Cid

3
c # 8 können Sie jetzt angeben, dass eine Objektvariable nicht null sein darf. Wenn Sie diesen Compiler - Flag Flip, müssen Sie jede Variable angeben, wird sein null erlaubt.
Jeremy Lakeman

Antworten:


66

Schritt für Schritt Erklärung:

params Delegate?[] delegates - Es ist ein Array von nullable Delegate

params Delegate?[]? delegates - Das gesamte Array kann nullwertfähig sein

Da jeder Parameter vom Typ ist Delegate?und Sie einen Index des Delegate?[]?Arrays zurückgeben, ist es sinnvoll, dass der Rückgabetyp Delegate?andernfalls vom Compiler einen Fehler zurückgegeben wird, als ob Sie ihn zurücksetzen würden, und intvon einer Methode, die eine Zeichenfolge zurückgibt.

Sie können beispielsweise Ihren Code ändern, um einen DelegateTyp wie diesen zurückzugeben:

public static Delegate Combine(params Delegate?[]? delegates)
{
    Delegate defaulDelegate = // someDelegate here
    if (delegates == null || delegates.Length == 0)
        return defaulDelegate;

    Delegate d = delegates[0] ?? defaulDelegate;
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}

4
Was ist mit meiner ersten Frage? Warum der Rückgabewert verwendet wird, ?obwohl er bereits Referenztyp (Klasse) ist
snr - Monica

2
Weil Sie d zurückgeben, welches ist Delegierter? Art. Und auch null oder delegieren je nach den Parametern
Athanasios Kataras

2
@snr Der Rückgabewert verwendet das ?aus genau demselben Grund, warum der Argumentwert das verwendet ?. Wenn Sie Letzteres verstehen, verstehen Sie Ersteres automatisch. Da die Methode möglicherweise null zurückgibt , akzeptiert der Parameter möglicherweise null . Das heißt, beide können null enthalten .
GSerg

2
Ich habe es berührt. Ich habe es gerade als zweiten Teil mit dem Beispiel beantwortet. Since each parameter is of the type Delegate? and you return an index of the Delegate?[]? array, then it makes sense that the return type is Delegate?
Athanasios Kataras

2
@snr: Warum wird der Rückgabewert verwendet? obwohl es sich bereits um einen Referenztyp (Klassentyp) handelt (1) Es kann eine Gewohnheit oder ein Refactoring-Rest sein, wenn Strukturen auch in ähnlichem Code verwendet werden. (2) In C # 8 kann nicht zugelassen werden, dass Referenztypen von Natur aus nullbar sind (was eine explizite Nullbarkeit erfordert ( 3) Foo?hat eine ordentliche HasValueEigenschaft, Foomuss aber == nullüberprüft werden. (4) Entwicklern, die die Methodensignatur lesen, wird mitgeteilt, dass Nullen ein mögliches, zu erwartendes und korrektes Ergebnis sind. (5) Der Code scheint von jemandem geschrieben worden zu sein, der ist sehr gern nullability und explizit darüber.
Flater

25

Nullable Reference Types sind neu in C # 8.0 und existieren vorher nicht.

Es ist eine Frage der Dokumentation und wie Warnungen zur Kompilierungszeit erzeugt werden.

Die Ausnahme "Objekt nicht auf eine Instanz eines Objekts festgelegt" ist recht häufig. Dies ist jedoch eine Laufzeitausnahme, die teilweise bereits zur Kompilierungszeit erkannt werden kann.

Für eine Regelung können Delegate dSie jederzeit anrufen

 d.Invoke();

Das heißt, Sie können es codieren, zur Kompilierungszeit wird nichts passieren. Zur Laufzeit können Ausnahmen ausgelöst werden.

Während für einen neuen Delegate? pdieser Code

 p.Invoke();

erzeugt eine Compiler-Warnung. CS8602: Dereference of a possibly null reference es sei denn, Sie schreiben:

 p?.Invoke();

Was bedeutet, rufen Sie nur an, wenn nicht null.

Sie dokumentieren also, dass eine Variable null enthalten kann oder nicht. Es löst früher Warnungen aus und kann mehrere Tests für null vermeiden. Das gleiche was du für int und int hast?. Sie wissen sicher, dass einer nicht null ist - und Sie wissen, wie man einen in den anderen konvertiert.


mit der Einschränkung, dass Sie nicht sicher wissen, dass Delegatedas nicht null ist. Sie tun einfach so, als ob Sie es sicher wissen (was in den meisten Fällen gut genug ist).
Tim Pohlmann

@TimPohlmann Neben int i = null ist auch ein String s = null unmöglich (in C # 8 mit dieser brechenden Änderung). Es ist also kaum mehr als etwas vorzutäuschen. Aus Gründen der Abwärtskompatibilität wurde es auf eine Warnung herabgestuft. Wenn Sie die Warnung auf einen Fehler aktualisieren, wissen Sie sicher, dass sie nicht null ist.
Holger

4
Das letzte Mal, als ich überprüft habe, dass NRTs nicht 100% kugelsicher sind. Es gibt bestimmte Randfälle, die der Compiler nicht erkennen kann.
Tim Pohlmann

Ja, aber dies ist dasselbe wie für Warnungen "Variable nicht initialisiert" oder "Nicht alle Codepfade, die einen Wert zurückgeben". Das sind alles Funktionen zu 100% zur Kompilierungszeit, ohne zur Laufzeit erkannt zu werden. Im Gegensatz zu int? Füge ich keinen zusätzlichen Speicher hinzu, um zusätzliche Informationen aufzunehmen. Nehmen wir also an, es ist so zuverlässig wie Intellisense. Ziemlich gut.
Holger

1
Wenn Sie eine haben int, kann es nie null sein. Wenn Sie eine haben Delegate, kann sie null sein (aus verschiedenen Gründen, z. B. Reflexion). Es ist normalerweise sicher anzunehmen, dass Delegate(in C # 8 mit aktiviertem NRT) nicht null ist, aber Sie wissen es nie genau (wo intwir es sicher wissen).
Tim Pohlmann

5

In C # 8 sollte man Referenztypen explizit als nullbar markieren.

Standardmäßig können diese Typen keine Null enthalten, ähnlich wie Werttypen. Dies ändert zwar nichts an der Funktionsweise unter der Haube, bei der Typprüfung müssen Sie dies jedoch manuell tun.

Der angegebene Code wurde für die Verwendung mit C # 8 überarbeitet, profitiert jedoch nicht von dieser neuen Funktion.

public static Delegate? Combine(params Delegate?[]? delegates)
{
    // ...[]? delegates - is not null-safe, so check for null and emptiness
    if (delegates == null || delegates.Length == 0)
        return null;

    // Delegate? d - is not null-safe too
    Delegate? d = delegates[0];
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}

Hier ist ein Beispiel für einen aktualisierten Code (funktioniert nicht, nur eine Idee), der diese Funktion nutzt. Es hat uns vor einer Nullprüfung bewahrt und diese Methode ein wenig vereinfacht.

public static Delegate? Combine(params Delegate[] delegates)
{
    // `...[] delegates` - is null-safe, so just check if array is empty
    if (delegates.Length == 0) return null;

    // `d` - is null-safe too, since we know for sure `delegates` is both not null and not empty
    Delegate d = delegates[0];

    for (int i = 1; i < delegates.Length; i++)
        // then here is a problem if `Combine` returns nullable
        // probably, we can add some null-checks here OR mark `d` as nullable
        d = Combine(d, delegates[i]);

    return d;
}

2
those types are not able to contain nullBeachten Sie, dass eine Compiler- Warnung und kein Fehler generiert wird. Der Code läuft einwandfrei (obwohl er möglicherweise NullReferenceExceptions generiert). Dies erfolgt unter Berücksichtigung der Abwärtskompatibilität.
JAD
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.