Markieren Sie Parameter in C # /. NET als NICHT nullbar. NET?

98

Gibt es ein einfaches Attribut oder einen Datenvertrag, den ich einem Funktionsparameter zuweisen kann, der verhindert, dass nuller in C # /. NET übergeben wird? Im Idealfall wird dies auch zur Kompilierungszeit überprüft, um sicherzustellen, dass das Literal nullnirgendwo dafür verwendet wird, und zur Laufzeit ArgumentNullException.

Zur Zeit schreibe ich so etwas wie ...

if (null == arg)
  throw new ArgumentNullException("arg");

... für jedes Argument, das ich nicht erwarte null.

In diesem Sinne gibt es ein Gegenteil davon, dass Nullable<>Folgendes fehlschlagen würde:

NonNullable<string> s = null; // throw some kind of exception
Neil C. Obremski
quelle
7
In neueren Versionen von C # funktioniert die Verwendung von "nameof ()" besser: Neue ArgumentNullException (nameof (arg)) auslösen; Auf diese Weise, wenn Sie den Namen umgestalten, wenn sich Ihre
Wurfaussage ändert
C # 8 unterstützt jetzt nullfähige Referenztypen. Dies ist eine Compileroption, die Sie in Ihrem Projekt aktivieren können. docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Paul Stegler

Antworten:

69

Zur Kompilierungszeit ist leider nichts verfügbar.

Ich habe eine etwas hackige Lösung, die ich kürzlich in meinem Blog gepostet habe und die eine neue Struktur und Konvertierungen verwendet.

In .NET 4.0 mit dem Code Contracts- Material wird das Leben viel schöner. Es wäre immer noch sehr schön, eine tatsächliche Sprachsyntax und Unterstützung in Bezug auf Nicht-Nullbarkeit zu haben, aber die Codeverträge werden viel helfen.

Ich habe auch eine Erweiterungsmethode in MiscUtil namens ThrowIfNull, die es ein bisschen einfacher macht.

Ein letzter Punkt - irgendein Grund für die Verwendung von " if (null == arg)" anstelle von " if (arg == null)"? Ich finde das letztere leichter zu lesen, und das Problem, das das erstere in C löst, gilt nicht für C #.

Jon Skeet
quelle
2
> Zur Kompilierungszeit ist leider nichts verfügbar. > ...> Es wäre immer noch sehr schön, eine tatsächliche Sprachsyntax und Unterstützung für die Nicht-Nullbarkeit zu haben, da stimme ich zu. Ich würde auch gerne sehen, dass Fehler bei der Kompilierung auftreten.
AndrewJacksonZA
2
@ Jon, funktioniert "if (arg = null)" nicht, wenn zufällig eine implizite Umwandlung in bool definiert ist? Ich gebe zu, es mag pervers erscheinen, aber es kompiliert ...
Thomas S. Trias
11
@ ThomasS.Trias: Ja, in diesem unglaublich obskuren Randfall, kombiniert mit einem Tippfehler, kombiniert mit einem Mangel an Tests rund um diesen Code, würden Sie ein Problem haben. Zu diesem Zeitpunkt denke ich, dass Sie größere Probleme haben :)
Jon Skeet
4
@ Jon: Zugegeben. Ich denke, ich werde meine geliebten Yoda-Bedingungen aufgeben. :-)
Thomas S. Trias
1
@SebastianMach: Ich glaube nicht, dass der Grund dafür if (null == x)in natürlichen Sprachunterschieden liegt. Ich glaube, es geht wirklich um einen veralteten Stil, der sich nur durch Beispiele usw. verbreitet.
Jon Skeet
21

Ich weiß, dass ich bei dieser Frage unglaublich spät dran bin, aber ich denke, dass die Antwort relevant wird, wenn die neueste Hauptiteration von C # der Veröffentlichung näher kommt und dann veröffentlicht wird. In C # 8.0 tritt eine wesentliche Änderung auf. In C # wird davon ausgegangen, dass alle Typen als nicht null betrachtet werden.

Laut Mads Torgersen:

Das Problem ist, dass Nullreferenzen so nützlich sind. In C # sind sie der Standardwert für jeden Referenztyp. Was wäre sonst der Standardwert? Welchen anderen Wert hätte eine Variable, bis Sie entscheiden können, was Sie ihr sonst noch zuweisen möchten? Mit welchem ​​anderen Wert könnten wir eine frisch zugewiesene Reihe von Referenzen versehen, bis Sie damit fertig sind, sie auszufüllen?

Manchmal ist null auch ein sinnvoller Wert für sich. Manchmal möchten Sie die Tatsache darstellen, dass beispielsweise ein Feld keinen Wert hat. Dass es in Ordnung ist, für einen Parameter „nichts“ zu übergeben. Der Schwerpunkt liegt jedoch manchmal auf. Und hier liegt ein weiterer Teil des Problems: In Sprachen wie C # können Sie nicht ausdrücken, ob eine Null hier eine gute Idee ist oder nicht.

Die von Mads skizzierte Entschließung lautet also:

  1. Wir glauben, dass es üblicher ist, dass eine Referenz nicht null ist. Nullable Referenztypen wären die seltenere Art (obwohl wir keine guten Daten haben, anhand derer wir feststellen können, um wie viel), daher sollten sie eine neue Anmerkung erfordern.

  2. Die Sprache hat bereits eine Vorstellung von - und eine Syntax für - nullbare Werttypen. Die Analogie zwischen beiden würde die Sprachaddition konzeptionell einfacher und sprachlich einfacher machen.

  3. Es scheint richtig, dass Sie sich oder Ihren Verbraucher nicht mit umständlichen Nullwerten belasten sollten, es sei denn, Sie haben aktiv entschieden, dass Sie sie möchten. Nullen, nicht das Fehlen von Nullen, sollten das sein, für das Sie sich ausdrücklich entscheiden müssen.

Ein Beispiel für die gewünschte Funktion:

public class Person
{
     public string Name { get; set; } // Not Null
     public string? Address { get; set; } // May be Null
}

Die Vorschau ist für die Vorschau von Visual Studio 2017, 15.5.4+ verfügbar.

Greg
quelle
Weiß jemand, ob dies jemals Teil von C # 8.0 wurde?
TroySteven
@TroySteven Ja, Sie müssen sich jedoch über die Einstellungen in Visual Studio dafür entscheiden.
Greg
@TroySteven Hier ist die Dokumentation dazu. docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Greg
Schön, es sieht so aus, als müssten Sie es aktivieren, bevor es funktioniert. Das ist gut für die Abwärtskompatibilität.
TroySteven
15

Ich weiß, dass dies eine SEHR alte Frage ist, aber diese fehlte hier:

Wenn Sie ReSharper / Rider verwenden, können Sie das Annotated Framework verwenden .

Bearbeiten : Ich habe gerade eine zufällige -1 für diese Antwort bekommen. Das ist gut. Beachten Sie jedoch, dass es immer noch gültig ist, auch wenn es nicht mehr der empfohlene Ansatz für C # 8.0 + -Projekte ist (um zu verstehen, warum, lesen Sie Gregs Antwort ).

rsenna
quelle
1
Interessanterweise enthält Ihre kompilierte Anwendung standardmäßig keinen Verweis auf die JetBrains.Annotations.dll, sodass Sie sie nicht mit der Anwendung verteilen müssen: Verwendung von JetBrains-Anmerkungen zur Verbesserung der ReSharper-Inspektionen
Lu55
9

Überprüfen Sie die Validatoren in der Unternehmensbibliothek. Sie können so etwas tun wie:

private MyType _someVariable = TenantType.None;
[NotNullValidator(MessageTemplate = "Some Variable can not be empty")]
public MyType SomeVariable {
    get {
        return _someVariable;
    }
    set {
        _someVariable = value;
    }
}

Dann in Ihrem Code, wenn Sie ihn validieren möchten:

Microsoft.Practices.EnterpriseLibrary.Validation.Validator myValidator = ValidationFactory.CreateValidator<MyClass>();

ValidationResults vrInfo = InternalValidator.Validate(myObject);
Nicht ich
quelle
0

nicht das schönste aber:

public static bool ContainsNullParameters(object[] methodParams)
{
     return (from o in methodParams where o == null).Count() > 0;
}

Mit der ContainsNullParameters-Methode können Sie auch kreativer werden:

public static bool ContainsNullParameters(Dictionary<string, object> methodParams, out ArgumentNullException containsNullParameters)
       {
            var nullParams = from o in methodParams
                             where o.Value == null
                             select o;

            bool paramsNull = nullParams.Count() > 0;


            if (paramsNull)
            {
                StringBuilder sb = new StringBuilder();
                foreach (var param in nullParams)
                    sb.Append(param.Key + " is null. ");

                containsNullParameters = new ArgumentNullException(sb.ToString());
            }
            else
                containsNullParameters = null;

            return paramsNull;
        }

Natürlich können Sie einen Abfangjäger oder eine Reflexion verwenden, aber diese sind mit geringem Aufwand leicht zu verfolgen / zu verwenden

Jeff Grizzle
quelle
3
Sehr schlechte Praxis mit solchen Abfragen: return (from o in methodParams where o == null).Count() > 0; Verwendung: return methodParams.Any(o=>o==null);Es wird viel schneller bei großen Sammlungen sein
Yavanosta
Warum die Ausnahme vertreiben? Warum nicht einfach werfen?
Martin Capodici
-4

Ok, diese Antwort ist etwas spät, aber hier ist, wie ich sie löse:

public static string Default(this string x)
{
    return x ?? "";
}

Verwenden Sie diese Erweiterungsmethode, dann können Sie null und leere Zeichenfolgen als dasselbe behandeln.

Z.B

if (model.Day.Default() == "")
{
    //.. Do something to handle no Day ..
}

Ich weiß, dass es nicht ideal ist, da Sie daran denken müssen, überall Standard zu nennen, aber es ist eine Lösung.

Martin Capodici
quelle
5
Wie ist das besser / einfacher als die (x == null) Prüfungen? oder die Funktion String.IsNotNullOrEmpty.
Batavia
Nicht viel besser als string.IsNotNullOrEmptywirklich anders als der Zucker, das, was dir wichtig ist, auf der linken Seite zu haben. Kann in andere Funktionen (Länge, Verkettung) usw. eingespeist werden. Etwas besser.
Martin Capodici
@MartinCapodici Dies erzeugt auch die Illusion, dass Sie die Instanzmethode ( Default()) auf null ( model.Day) sicher aufrufen können . Ich weiß, dass Erweiterungsmethoden nicht gegen Null geprüft werden, aber meine Augen sind sich dessen nicht bewusst und sie haben sich das bereits ?im Code vorgestellt: model?.Day?.Default():)
Alb