if (myvar != null){// Go ahead and use myvarmyvar.property = ...}else{// Whoops! myvar is null and cannot be used without first// assigning it to an instance reference// Attempting to use myvar here will result in NullReferenceException}
开发人员是null9请注意,C#具有变量可为空数据类型的概念(例如数据库表可以具有可为空的字段)-您可以将null分配给它们以表示其中没有存储任何值,例如int? a = null;(这是Nullable<int> a = null;的快捷方式),其中问号表示允许将null存储在变量a中。您可以使用if (a.HasValue) {...}或if (a==null) {...}进行检查。可为空的变量,像a这个例子,允许通过a.Value显式访问值,或者通过a正常访问。 int? a = null;0通过a.Value访问它会抛出一个null2而不是null3,如果a是null-你应该事先做检查,即如果你有另一个不可为空的变量null6,那么你应该做像null7或更短的null8这样的赋值。
public class Person{public int Age { get; set; }}public class Book{public Person Author { get; set; }}public class Example{public void Foo(){Book b1 = new Book();int authorAge = b1.Author.Age; // You never initialized the Author property.// there is no Person to get an Age from.}}
public class Person{public ICollection<Book> Books { get; set; }}public class Book{public string Title { get; set; }}
嵌套集合Initializers的行为相同:
Person p1 = new Person{Books = {new Book { Title = "Title1" },new Book { Title = "Title2" },}};
这翻译为:
Person p1 = new Person();p1.Books.Add(new Book { Title = "Title1" });p1.Books.Add(new Book { Title = "Title2" });
new Person只创建Person的实例,但Books集合仍然是null。集合Initializer语法不会创建集合对于p1.Books,它只转换为p1.Books.Add(...)语句。
数组
int[] numbers = null;int n = numbers[0]; // numbers is null. There is no array to index.
数组元素
Person[] people = new Person[5];people[0].Age = 20 // people[0] is null. The array was allocated but not// initialized. There is no Person to set the Age for.
锯齿状数组
long[][] array = new long[1][];array[0][0] = 3; // is null because only the first dimension is yet initialized.// Use array[0] = new long[2]; first.
收藏/列表/词典
Dictionary<string, int> agesForNames = null;int age = agesForNames["Bob"]; // agesForNames is null.// There is no Dictionary to perform the lookup.
范围变量(间接/延迟)
public class Person{public string Name { get; set; }}var people = new List<Person>();people.Add(null);var names = from p in people select p.Name;string firstName = names.First(); // Exception is thrown here, but actually occurs// on the line above. "p" is null because the// first element we added to the list is null.
事件(C#)
public class Demo{public event EventHandler StateChanged;
protected virtual void OnStateChanged(EventArgs e){StateChanged(this, e); // Exception is thrown here// if no event handlers have been attached// to StateChanged event}}
public class Form1{private Customer customer;
private void Form1_Load(object sender, EventArgs e){Customer customer = new Customer();customer.Name = "John";}
private void Button_Click(object sender, EventArgs e){MessageBox.Show(customer.Name);}}
这可以通过遵循约定以下划线作为字段前缀来解决:
private Customer _customer;
ASP.NET页面生命周期:
public partial class Issues_Edit : System.Web.UI.Page{protected TestIssue myIssue;
protected void Page_Load(object sender, EventArgs e){if (!IsPostBack){// Only called on first load, not when button clickedmyIssue = new TestIssue();}}
protected void SaveButton_Click(object sender, EventArgs e){myIssue.Entry = "NullReferenceException here!";}}
ASP.NET会话值
// if the "FirstName" session value has not yet been set,// then this line will throw a NullReferenceExceptionstring firstName = Session["FirstName"].ToString();
// Controllerpublic class Restaurant:Controller{public ActionResult Search(){return View(); // Forgot the provide a Model here.}}
// Razor view@foreach (var restaurantSearch in Model.RestaurantSearch) // Throws.{}
<p>@Model.somePropertyName</p> <!-- Also throws -->
string GetCategory(string bookTitle){var book = library.FindBook(bookTitle); // This may return nullif (book == null)throw new BookNotFoundException(bookTitle); // Your custom exceptionreturn book.Category;}
string GetTitle(int knownBookID){// You know this should never return null.var book = library.GetBook(knownBookID);
// Exception will occur on the next line instead of at the end of this method.Debug.Assert(book != null, "Library didn't return a book for known book ID.");
// Some other code
return book.Title; // Will never throw NullReferenceException in Debug mode.}
DateTime? appointment = null;Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));// Will display the default value provided (DateTime.Now), because appointment is null.
appointment = new DateTime(2022, 10, 20);Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));// Will display the appointment date, not the default
使用空合并运算符:??[C#]或If()[VB]。
当遇到null时提供默认值的简写:
IService CreateService(ILogger log, Int32? frobPowerLevel){var serviceImpl = new MyService(log ?? NullLog.Instance);
// Note that the above "GetValueOrDefault()" can also be rewritten to use// the coalesce operator:serviceImpl.FrobPowerLevel = frobPowerLevel ?? 5;}
// regular null checkint titleLength = 0;if (title != null)titleLength = title.Length; // If title is null, this would throw NullReferenceException
// combining the `?` and the `??` operatorint titleLength = title?.Length ?? 0;
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count){for (int i = 0; i < count; ++i)yield return f.MakeFrob();}...FrobFactory factory = whatever;IEnumerable<Frobs> frobs = GetFrobs();...foreach(Frob frob in frobs) { ... }
如果whatever导致null,那么MakeFrob将抛出。现在,你可能认为正确的做法是:
// DON'T DO THISpublic IEnumerable<Frob> GetFrobs(FrobFactory f, int count){if (f == null)throw new ArgumentNullException("f", "factory must not be null");for (int i = 0; i < count; ++i)yield return f.MakeFrob();}
// DO THISpublic IEnumerable<Frob> GetFrobs(FrobFactory f, int count){// No yields in a public method that throws!if (f == null)throw new ArgumentNullException("f", "factory must not be null");return GetFrobsForReal(f, count);}private IEnumerable<Frob> GetFrobsForReal(FrobFactory f, int count){// Yields in a private methodDebug.Assert(f != null);for (int i = 0; i < count; ++i)yield return f.MakeFrob();}
string testString = null; //Because it doesn't have a value (i.e. it's null; "Length" cannot do what it needs to do)
if (testString.Length == 0) // Throws a nullreferenceexception{//Do something}
在这里,SelectedDate实际上是Calendar Web Control类型的DateTime类型的属性,绑定可以完美地返回null。隐式ASP.NET生成器将创建一段与上面的转换代码等效的代码。这将引发一个很难发现的NullReferenceException,因为它位于ASP.NET生成的代码中,编译良好…
class Book {public string Name { get; set; }}class Car { }
Car mycar = new Car();Book mybook = mycar as Book; // Incompatible conversion --> mybook = null
Console.WriteLine(mybook.Name); // NullReferenceException
Contact contact = new Contact { Name = "Abhinav"};var context = new DataContext();context.Contacts.Add(contact);context.SaveChanges(); // NullReferenceException at this line
为了完整起见DataContext类
public class DataContext : DbContext{public DbSet<Contact> Contacts {get; set;}}
和联系实体类。有时实体类是部分类,以便您也可以在其他文件中扩展它们。
public partial class Contact{public string Name {get; set;}}
public class MyController{private ServiceA serviceA;private ServiceB serviceB;
public MyController(ServiceA serviceA, ServiceB serviceB){this.serviceA = serviceA;this.serviceB = serviceB;}
public void MyMethod(){// We don't need to check null because the dependency injection container// injects it, provided you took care of bootstrapping it.var someObject = serviceA.DoThis();}}
Private arr as String() = New String(10){}' orPrivate arr() As String = New String(10){}
' For a local array (in a procedure) and using 'Option Infer':Dim arr = New String(10) {}
Private myDoubles As Double() = {1.5, 2, 9.9, 18, 3.14}
示例:类对象数组
Dim arrFoo(5) As Foo
For i As Integer = 0 To arrFoo.Count - 1arrFoo(i).Bar = i * 10 ' ExceptionNext
数组已创建,但其中的Foo对象尚未创建。
补救措施
For i As Integer = 0 To arrFoo.Count - 1arrFoo(i) = New Foo() ' Create Foo instancearrFoo(i).Bar = i * 10Next
使用List(Of T)将使没有有效对象的元素变得非常困难:
Dim FooList As New List(Of Foo) ' List created, but it is emptyDim f As Foo ' Temporary variable for the loop
For i As Integer = 0 To 5f = New Foo() ' Foo instance createdf.Bar = i * 10FooList.Add(f) ' Foo object added to listNext
Private myList As List(Of String)..myList.Add("ziggy") ' NullReference
你会因为同样的原因得到同样的异常-myList只声明了,但没有创建实例。补救措施是一样的:
myList = New List(Of String)
' Or create an instance when declared:Private myList As New List(Of String)
一个常见的疏忽是一个使用集合Type的类:
Public Class FooPrivate barList As List(Of Bar)
Friend Function BarCount As IntegerReturn barList.CountEnd Function
Friend Sub AddItem(newBar As Bar)If barList.Contains(newBar) = False ThenbarList.Add(newBar)End IfEnd Function
Dim da As OleDbDataAdapterDim ds As DataSetDim MaxRows As Integer
con.Open()Dim sql = "SELECT * FROM tblfoobar_List"da = New OleDbDataAdapter(sql, con)da.Fill(ds, "foobar")con.Close()
MaxRows = ds.Tables("foobar").Rows.Count ' Error
If ds.Tables(0).Rows.Count > 0 ThentxtID.Text = ds.Tables(0).Rows(0).Item(1)txtID.Name = ds.Tables(0).Rows(0).Item(2)End If
Fill是一个返回Rows受影响的数量的函数,也可以测试:
If da.Fill(ds, "Employees") > 0 Then...
例3
Dim da As New OleDb.OleDbDataAdapter("SELECT TICKET.TICKET_NO,TICKET.CUSTOMER_ID, ... FROM TICKET_RESERVATION AS TICKET INNER JOINFLIGHT_DETAILS AS FLIGHT ... WHERE [TICKET.TICKET_NO]= ...", con)Dim ds As New DataSetda.Fill(ds)
If ds.Tables("TICKET_RESERVATION").Rows.Count > 0 Then
Dim expiry As DateTime ' for text date validationIf (ComboBox5.SelectedItems.Count > 0) AndAlso(ListBox1.SelectedItems.Count > 0) AndAlso(ComboBox2.SelectedItems.Count > 0) AndAlso(DateTime.TryParse(expiry.Text, expiry) Then
'... do stuffElseMessageBox.Show(...error message...)End If
Public Class Form1
Private NameBoxes = New TextBox(5) {Controls("TextBox1"), _Controls("TextBox2"), Controls("TextBox3"), _Controls("TextBox4"), Controls("TextBox5"), _Controls("TextBox6")}
' same thing in a different format:Private boxList As New List(Of TextBox) From {TextBox1, TextBox2, TextBox3 ...}
' Immediate NRE:Private somevar As String = Me.Controls("TextBox1").Text
Sub Form_Load(..._'...Dim name As String = NameBoxes(2).Text ' NRE' ...' More code (which will likely not be executed)' ...End Sub
说明这适用于任何和所有控件和组件引用,使它们成为非法:
Public Class Form1
Private myFiles() As String = Me.OpenFileDialog1.FileName & ...Private dbcon As String = OpenFileDialog1.FileName & ";Jet Oledb..."Private studentName As String = TextBox13.Text
' Module level declarationPrivate NameBoxes as TextBox()Private studentName As String
' Form Load, Form Shown or Sub New:'' Using the OP's approach (illegal using OPTION STRICT)NameBoxes = New TextBox() {Me.Controls("TextBox1"), Me.Controls("TestBox2"), ...)studentName = TextBox32.Text ' For simple control references
' DeclarationPrivate NameBoxes As TextBox()
' Initialization - simple and easy to read, hard to botch:NameBoxes = New TextBox() {TextBox1, TextBox2, ...)
' Initialize a ListNamesList = New List(Of TextBox)({TextBox1, TextBox2, TextBox3...})' orNamesList = New List(Of TextBox)NamesList.AddRange({TextBox1, TextBox2, TextBox3...})
函数不返回任何东西
Private bars As New List(Of Bars) ' Declared and created
Public Function BarList() As List(Of Bars)bars.ClearIf someCondition ThenFor n As Integer = 0 to someValuebars.Add(GetBar(n))Next nElseExit FunctionEnd If
Return barsEnd Function
If (row.Cells(0) IsNot Nothing) AndAlso (IsDBNull(row.Cells(0).Value) = False) Then
例2
Dim getFoo = (From f In dbContext.FooBarsWhere f.something = somethingSelect f).FirstOrDefault
If Not IsDBNull(getFoo) ThenIf IsDBNull(getFoo.user_id) ThentxtFirst.Text = getFoo.first_nameElse...
FirstOrDefault返回第一项或默认值,对于引用类型为Nothing,从不为DBNull:
If getFoo IsNot Nothing Then...
控件
Dim chk As CheckBox
chk = CType(Me.Controls(chkName), CheckBox)If chk.Checked ThenReturn chkEnd If
@inherits System.Web.Mvc.WebViewPage@{ViewBag.Title = "Entity Index";List<MyEntity> MyEntities = new List<MyEntity>();MyEntities.Add(new MyEntity());MyEntities.Add(new MyEntity());MyEntities.Add(new MyEntity());}<div>@{foreach(var M in MyEntities){// Squiggly lines below. Hovering says: cannot convert method group 'partial' to non-delegate type Object, did you intend to envoke the Method?@Html.Partial("MyOtherView.cshtml");}}</div>
The hardest one .. if the GC collected the object already... This generally occurs if you are trying to find an object using strings... That is, finding it by name of the object then it may happen that the GC might already cleaned it up... This is hard to find and will become quite a problem... A better way to tackle this is do null checks wherever necessary during the development process. This will save you a lot of time.
By finding by name I mean some framework allow you to FIndObjects using strings and the code might look like this: FindObject("ObjectName");
[System.Diagnostics.DebuggerNonUserCode]public struct NotNull<T> where T: class{private T _value;
public T Value{get{if (_value == null){throw new Exception("null value not allowed");}
return _value;}set{if (value == null){throw new Exception("null value not allowed.");}
_value = value;}}
public static implicit operator T(NotNull<T> notNullValue){return notNullValue.Value;}
public static implicit operator NotNull<T>(T value){return new NotNull<T> { Value = value };}}
NotNull<Person> person = null; // throws exceptionNotNull<Person> person = new Person(); // OKNotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null
Person person = new Person { Name = "John" };WriteName(person);
public static void WriteName(NotNull<Person> person){Console.WriteLine(person.Value.Name);}
Person person = (NotNull<Person>)GetPerson();
public static Person GetPerson(){return new Person { Name = "John" };}
结合扩展
将NotNull<T>与扩展方法结合起来,你可以涵盖更多的情况。这是一个扩展方法的示例:
[System.Diagnostics.DebuggerNonUserCode]public static class NotNullExtension{public static T NotNull<T>(this T @this) where T: class{if (@this == null){throw new Exception("null value not allowed");}
return @this;}}
想象一下,country是一个类型为Country的对象,它有一个名为State的属性,依此类推。如果country、State、County或City是null,那么address will benull. Therefore you only have to check whether地址Country0null'。
class Program{static void Main(string[] args){MyClass1 obj;obj.foo(); // Use of unassigned local variable 'obj'}}
public class MyClass1{internal void foo(){Console.WriteLine("Hello from foo");}}