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; }
}
嵌套集合初始值设定项的行为相同:
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。集合初始值设定项语法不会为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.
Collection/List/Dictionary
Dictionary<string, int> agesForNames = null;
int age = agesForNames["Bob"]; // agesForNames is null.
// There is no Dictionary to perform the lookup.
范围变量(Indirect/Deferred)
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.
活动
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 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 clicked
myIssue = 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 NullReferenceException
string firstName = Session["FirstName"].ToString();
ASP.NET MVC 空视图模型
如果在 ASP.NET MVC 视图中引用@Model的属性时发生异常,则需要了解return在操作方法中设置,当您查看视图时。从控制器返回空模型(或模型属性)时,视图访问它时会发生异常:
// Controller
public 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(Book b) {
if (b == null)
return "Unknown";
return b.Category;
}
从方法调用中显式检查 null 并抛出自定义异常。
你也可以抛出一个自定义异常,只是为了在调用代码中捕获它:
string GetCategory(string bookTitle) {
var book = library.FindBook(bookTitle); // This may return null
if (book == null)
throw new BookNotFoundException(bookTitle); // Your custom exception
return 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
使用 null 合并运算符:?? [137]或 If() [138]。
遇到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 check
int titleLength = 0;
if (title != null)
titleLength = title.Length; // If title is null, this would throw NullReferenceException
// combining the `?` and the `??` operator
int titleLength = title?.Length ?? 0;
同样,对于数组,您可以使用?[i],如下所示:
int[] myIntArray=null;
var i=5;
int? elem = myIntArray?[i];
if (!elem.HasValue) Console.WriteLine("No value");
这将执行以下操作:如果 myIntArray 为 null,则表达式返回 null,您可以安全地检查它。如果它包含一个数组,它将执行相同的操作:elem = myIntArray[i];并返回第 i 个元素。
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 THIS
public 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 THIS
public 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 method
Debug.Assert(f != null);
for (int i = 0; i < count; ++i)
yield return f.MakeFrob();
}
Private arr as String() = New String(10){}
' or
Private arr() As String = New String(10){}
' For a local array (in a procedure) and using 'Option Infer':
Dim arr = New String(10) {}
注意:从 VS 2010 开始,当使用文字和Option Infer初始化本地数组时,As <Type>和New元素是可选的:
Dim myDbl As Double() = {1.5, 2, 9.9, 18, 3.14}
Dim myDbl = New Double() {1.5, 2, 9.9, 18, 3.14}
Dim myDbl() = {1.5, 2, 9.9, 18, 3.14}
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 - 1
arrFoo(i).Bar = i * 10 ' Exception
Next
数组已创建,但其中的Foo对象没有。
补救
For i As Integer = 0 To arrFoo.Count - 1
arrFoo(i) = New Foo() ' Create Foo instance
arrFoo(i).Bar = i * 10
Next
使用List(Of T)将使得没有有效对象的元素变得非常困难:
Dim FooList As New List(Of Foo) ' List created, but it is empty
Dim f As Foo ' Temporary variable for the loop
For i As Integer = 0 To 5
f = New Foo() ' Foo instance created
f.Bar = i * 10
FooList.Add(f) ' Foo object added to list
Next
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 Foo
Private barList As List(Of Bar)
Friend Function BarCount As Integer
Return barList.Count
End Function
Friend Sub AddItem(newBar As Bar)
If barList.Contains(newBar) = False Then
barList.Add(newBar)
End If
End Function
Dim da As OleDbDataAdapter
Dim ds As DataSet
Dim 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
ds = New DataSet
da = New OleDBDataAdapter(sql, con)
da.Fill(ds, "Employees")
txtID.Text = ds.Tables("Employee").Rows(0).Item(1)
txtID.Name = ds.Tables("Employee").Rows(0).Item(2)
输入错误是一个问题:Employees vs Employee。没有创建名为“Employee”的DataTable,因此NullReferenceException结果试图访问它。另一个潜在的问题是假设当 SQL 包含 WHERE 子句时会有Items这可能不是这样。
补救
由于这使用一个表,因此使用Tables(0)将避免拼写错误。检查Rows.Count也可以帮助:
If ds.Tables(0).Rows.Count > 0 Then
txtID.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 JOIN
FLIGHT_DETAILS AS FLIGHT ... WHERE [TICKET.TICKET_NO]= ...", con)
Dim ds As New DataSet
da.Fill(ds)
If ds.Tables("TICKET_RESERVATION").Rows.Count > 0 Then
Dim expiry As DateTime ' for text date validation
If (ComboBox5.SelectedItems.Count > 0) AndAlso
(ListBox1.SelectedItems.Count > 0) AndAlso
(ComboBox2.SelectedItems.Count > 0) AndAlso
(DateTime.TryParse(expiry.Text, expiry) Then
'... do stuff
Else
MessageBox.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
' Module level declaration
Private 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
' Declaration
Private NameBoxes As TextBox()
' Initialization - simple and easy to read, hard to botch:
NameBoxes = New TextBox() {TextBox1, TextBox2, ...)
' Initialize a List
NamesList = New List(Of TextBox)({TextBox1, TextBox2, TextBox3...})
' or
NamesList = 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.Clear
If someCondition Then
For n As Integer = 0 to someValue
bars.Add(GetBar(n))
Next n
Else
Exit Function
End If
Return bars
End Function
bList = myFoo.BarList()
If bList IsNot Nothing Then...
执行不力 Try/Catch
糟糕实施的 Try/Catch 可以隐藏问题所在并导致新问题:
Dim dr As SqlDataReader
Try
Dim lnk As LinkButton = TryCast(sender, LinkButton)
Dim gr As GridViewRow = DirectCast(lnk.NamingContainer, GridViewRow)
Dim eid As String = GridView1.DataKeys(gr.RowIndex).Value.ToString()
ViewState("username") = eid
sqlQry = "select FirstName, Surname, DepartmentName, ExtensionName, jobTitle,
Pager, mailaddress, from employees1 where username='" & eid & "'"
If connection.State <> ConnectionState.Open Then
connection.Open()
End If
command = New SqlCommand(sqlQry, connection)
'More code fooing and barring
dr = command.ExecuteReader()
If dr.Read() Then
lblFirstName.Text = Convert.ToString(dr("FirstName"))
...
End If
mpe.Show()
Catch
Finally
command.Dispose()
dr.Close() ' <-- NRE
connection.Close()
End Try
If (row.Cells(0) IsNot Nothing) AndAlso (IsDBNull(row.Cells(0).Value) = False) Then
例 2
Dim getFoo = (From f In dbContext.FooBars
Where f.something = something
Select f).FirstOrDefault
If Not IsDBNull(getFoo) Then
If IsDBNull(getFoo.user_id) Then
txtFirst.Text = getFoo.first_name
Else
...
xlWorkSheet = xlWorkBook.Sheets("sheet1")
For i = 0 To myDGV.RowCount - 1
For j = 0 To myDGV.ColumnCount - 1
For k As Integer = 1 To myDGV.Columns.Count
xlWorkSheet.Cells(1, k) = myDGV.Columns(k - 1).HeaderText
xlWorkSheet.Cells(i + 2, j + 1) = myDGV(j, i).Value.ToString()
Next
Next
Next
这里,SelectedDate实际上是Calendar Web 控件类型的DateTime类型的属性,并且绑定可以完美地返回 null。隐式 ASP.NET Generator 将创建一段代码,它将等同于上面的强制转换代码。这会引发一个很难发现的NullReferenceException,因为它位于 ASP.NET 生成的代码中,编译得很好......
if (myvar != null)
{
// Go ahead and use myvar
myvar.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
}
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
}
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
这里,Book和Car是不兼容的类型; a Car不能 converted/cast 到Book。当此强制转换失败时,as返回null。在此之后使用mybook会导致NullReferenceException。
通常,您应该使用强制转换或as,如下所示:
如果您希望类型转换始终成功(即您知道对象应该提前是什么),那么您应该使用强制转换:
ComicBook cb = (ComicBook)specificBook;
如果您不确定类型,但想要尝试将其用作特定类型,请使用as:
ComicBook cb = specificBook as ComicBook;
if (cb != null) {
// ...
}
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();
}
}
[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 exception
NotNull<Person> person = new Person(); // OK
NotNull<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 be null . Therefore you only have to check whether地址is null`。
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");
}
}
30 回答
原因是什么?
底线
您正在尝试使用
null
(或 VB.NET 中的Nothing
)。这意味着您要么将其设置为null
,要么根本不将其设置为任何内容。像其他任何东西一样,
null
会被传递。如果它在方法“A”中是null
,则可能是方法“B”将null
传递给方法“A”。null
可以有不同的含义:对象变量未初始化因此**指向任何内容.**在这种情况下,如果访问此类对象的属性或方法,则会导致
NullReferenceException
。开发人员**故意使用 null 表示没有可用的有意义的值.**请注意,C#具有变量的可空数据类型的概念(如数据库表可以有可空字段) - 您可以为它们分配
null
以指示存在没有存储在其中的值,例如int? a = null;
,其中问号表示允许在变量a
中存储 null。您可以使用if (a.HasValue) {...}
或if (a==null) {...}
进行检查。可以为 Nullable 的变量(如本例中的a
)允许通过a.Value
显式访问该值,或者通过a
正常访问该值。注意通过
a.Value
访问它会抛出InvalidOperationException
而不是NullReferenceException
如果a
是null
- 你应该事先进行检查,i.e。如果你有另一个 on-nullable 变量int b;
那么你应该做if (a.HasValue) { b = a.Value; }
或更短的if (a != null) { b = a; }
等分配。本文的其余部分将详细介绍并显示许多程序员经常犯的错误,这些错误可导致
NullReferenceException
。进一步来说
运行时抛出
NullReferenceException
总是表示相同的事情:您正在尝试使用引用,并且引用未初始化(或者它曾被初始化,但不再初始化)。这意味着引用是
null
,并且您无法通过null
引用访问成员(例如方法)。最简单的情况:这将在第二行抛出
NullReferenceException
,因为您无法在指向null
的string
引用上调用实例方法ToUpper()
。调试
你如何找到
NullReferenceException
的来源?除了查看将在其发生的位置准确抛出的异常本身外,Visual Studio 中调试的一般规则适用:放置策略断点和检查你的变量,将鼠标悬停在其名称上,打开(Quick)Watch 窗口)或使用各种调试面板,如本地和汽车。如果要查找参考设置或未设置的位置,请 right-click 其名称并选择“查找所有参考”。然后,您可以在每个找到的位置放置一个断点,并在附加调试器的情况下运行程序。每次调试器在这样的断点上中断时,您需要确定是否期望引用为 non-null,检查变量并验证它是否指向实例。
通过这种方式跟踪程序流,您可以找到实例不应为 null 的位置,以及未正确设置的原因。
例子
可以抛出异常的一些常见场景:
通用
如果 ref1 或 ref2 或 ref3 为 null,那么你将获得
NullReferenceException
。如果你想解决这个问题,那么通过将表达式重写为更简单的等价物来找出哪一个是 null:具体来说,在
HttpContext.Current.User.Identity.Name
中,HttpContext.Current
可以为 null,或者User
属性可以为 null,或者Identity
属性可以为 null。间接
如果要避免子(Person)null 引用,可以在父(Book)对象的构造函数中初始化它。
嵌套对象初始化器
这同样适用于嵌套对象初始值设定项:
这转化为
虽然使用了
new
关键字,但它只创建Book
的新实例,而不是Person
的新实例,所以Author
属性仍为null
。嵌套集合初始化器
嵌套集合初始值设定项的行为相同:
这转化为
new Person
只创建Person
的实例,但Books
集合仍然是null
。集合初始值设定项语法不会为p1.Books
创建集合,它只会转换为p1.Books.Add(...)
语句。排列
数组元素
锯齿状阵列
Collection/List/Dictionary
范围变量(Indirect/Deferred)
活动
错误的命名约定:
如果您以不同于本地的方式命名字段,您可能已经意识到您从未初始化该字段。
这可以通过遵循约定前缀字段使用下划线来解决:
ASP.NET 页面生命周期:
ASP.NET 会话值
ASP.NET MVC 空视图模型
如果在 ASP.NET MVC 视图中引用
@Model
的属性时发生异常,则需要了解return
在操作方法中设置,当您查看视图时。从控制器返回空模型(或模型属性)时,视图访问它时会发生异常:WPF 控件创建顺序和事件
WPF 控件是在调用
InitializeComponent
期间按照它们在可视树中出现的顺序创建的。对于带有事件处理程序等的 early-created 控件,将引发NullReferenceException
,在InitializeComponent
期间引发 late-created 控件。例如 :
这里
comboBox1
在label1
之前创建。如果comboBox1_SelectionChanged
试图引用`label1,它将不会被创建。更改 XAML 中声明的顺序(i.e。,在
comboBox1
之前列出label1
,忽略设计哲学问题,至少会解决NullReferenceException
。与 as 一起演员
这不会抛出 InvalidCastException,但在转换失败时返回
null
(当 someObject 本身为 null 时)。所以要注意这一点。LINQ FirstOrDefault()和 SingleOrDefault()
没有任何内容时,普通版本
First()
和Single()
会抛出异常。在这种情况下,“OrDefault”版本返回 null。所以要注意这一点。foreach
当您尝试迭代 null 集合时会抛出
foreach
。通常由返回集合的方法的意外null
结果引起。更现实的例子 - 从 XML 文档中选择节点。如果找不到节点但是初始调试显示所有属性都有效,则抛出:
避免的方法
显式检查 null 并忽略空值。
如果您希望引用有时为 null,则可以在访问实例成员之前检查它是
null
:显式检查 null 并提供默认值。
方法调用您希望返回一个实例可以返回
null
,例如当找不到要查找的对象时。在这种情况下,您可以选择返回默认值:从方法调用中显式检查 null 并抛出自定义异常。
你也可以抛出一个自定义异常,只是为了在调用代码中捕获它:
如果值永远不应为 null,则使用 Debug.Assert,以便在发生异常之前捕获问题。
当您在开发过程中知道方法可能,但永远不应该返回
null
时,您可以在发生时尽快使用Debug.Assert()
中断:虽然这会检查不会在你的发布版本中结束,导致它在发布模式下在运行时
book == null
再次抛出NullReferenceException
。将 GetValueOrDefault()用于可空值类型,以便在它们为 null 时提供默认值。
使用 null 合并运算符:?? [137]或 If() [138]。
遇到
null
时提供默认值的简写:使用 null 条件运算符:?.或者?[142]用于数组(在 C#6 和 VB.NET14 中可用):
这有时也称为安全导航或 Elvis(在其形状之后)运算符。如果运算符左侧的表达式为 null,则不会计算右侧,而是返回 null。这意味着这样的情况:
如果此人没有标题,则会抛出异常,因为它试图在具有空值的属性上调用
ToUpper
。在 C#5 及以下版本中,可以通过以下方式保护:
现在 title 变量将为 null 而不是抛出异常。 C#6 为此引入了更短的语法:
这将导致 title 变量为
null
,如果person.Title
为null
,则不会调用ToUpper
。当然,您仍然需要检查
title
是否为 null 或使用 null 条件运算符和空合并运算符(??
)来提供默认值:同样,对于数组,您可以使用
?[i]
,如下所示:这将执行以下操作:如果 myIntArray 为 null,则表达式返回 null,您可以安全地检查它。如果它包含一个数组,它将执行相同的操作:
elem = myIntArray[i];
并返回第 i 个元素。使用空上下文(在 C#8 中可用):
在 C#8 中引入了 null context 和 nullable 引用类型对变量执行静态分析,并在值可能为 null 或已设置为 null 时提供编译器警告。可空引用类型允许明确允许类型为 null。
可以使用 csproj 文件中的 Nullable 元素为项目设置可为空的注释上下文和可为空的警告上下文。此元素配置编译器如何解释类型的可为空性以及生成哪些警告。有效设置为:
enable:启用可为空的注释上下文。可以为空的警告上下文启用。引用类型的变量,例如字符串,是 non-nullable。启用所有可空性警告。
disable:禁用可为空的注释上下文。可以为空的警告上下文被禁用。与早期版本的 C#一样,引用类型的变量是不经意的。所有可空性警告均被禁用。
safeonly:启用可为空的注释上下文。可以为空的警告语境是安全的。引用类型的变量是不可空的。启用所有安全可空性警告。
警告:禁用可为空的注释上下文。可以为空的警告上下文启用。参考类型的变量是遗忘的。启用所有可空性警告。
safeonlywarnings:禁用可为空的注释上下文。可以为空的警告语境是安全的。参考类型的变量是遗忘的。启用所有安全可空性警告。
使用与可空值类型相同的语法记录可空引用类型:a
?
附加到变量的类型。在迭代器中调试和修复 null derefs 的特殊技术
C#支持“迭代器块”(在某些其他流行语言中称为“生成器”)。由于延迟执行,空取消引用异常在迭代器块中调试尤其棘手:
如果
whatever
导致null
,那么MakeFrob
将抛出。现在,您可能认为正确的做法是:为什么这是错的?因为迭代器块实际上直到
foreach
才会运行!对GetFrobs
的调用只返回一个对象,迭代后它将运行迭代器块。通过像这样编写空检查可以防止空取消引用,但是将空参数异常移动到迭代点,而不是调用点,这对调试来说非常混乱。
正确的解决方法是:
也就是说,创建一个具有迭代器块逻辑的私有辅助方法,以及一个执行空检查并返回迭代器的公共表面方法。现在,当调用
GetFrobs
时,立即进行空检查,然后在迭代序列时执行GetFrobsForReal
。如果检查 LINQ to Objects 的参考源,您将看到始终使用此技术。它写得稍微笨拙,但它使得调试无效性错误变得更加容易。 优化您的代码以方便调用者,而不是作者的便利。
关于不安全代码中的空引用的说明
C#具有“不安全”模式,顾名思义,这种模式极其危险,因为不强制执行提供存储器安全性和类型安全性的正常安全机制。 你不应该编写不安全的代码,除非你对内存的工作原理有深入的了解。
在不安全模式下,您应该了解两个重要事实:
取消引用 null 指针产生与解除引用 null引用相同的异常
取消引用无效的 non-null 指针可以在某些情况下产生该异常
要理解为什么,首先要了解.NET 如何产生空取消引用异常。 (这些详细信息适用于在 Windows 上运行的.NET;其他操作系统使用类似的 mechanisms.)
内存在 Windows 中虚拟化;每个进程获得操作系统跟踪的许多“页面”内存的虚拟内存空间。每页内存都设置了标志,用于确定如何使用它:读取,写入,执行等。最低页面标记为“如果以任何方式使用会产生错误”。
C#中的空指针和空引用都在内部表示为数字零,因此任何将其取消引用到其相应的内存存储中的尝试都会导致操作系统产生错误。然后.NET 运行时检测到此错误并将其转换为空解除引用异常。
这就是为什么解除引用空指针和空引用会产生相同的异常。
第二点怎么样?取消引用位于虚拟内存最低页面的任何无效指针会导致相同的操作系统错误,从而导致相同的异常。
为什么这有意义?好吧,假设我们有一个包含两个 int 的结构,一个等于 null 的非托管指针。如果我们尝试取消引用结构中的第二个 int,CLR 将不会尝试访问零位置的存储;它将访问位置四的存储。但从逻辑上讲,这是一个空取消引用,因为我们通过 null 到达该地址。
如果您正在处理不安全的代码并且您获得了空解除引用异常,请注意违规指针不必为空。它可以是最低页面中的任何位置,并且将生成此异常。
NullReference 异常 - Visual Basic
Visual Basic的
NullReference Exception
与**C#**中的NullReference Exception
没有什么不同。毕竟,他们都报告了他们都使用的.NET 框架中定义的相同异常。 Visual Basic 特有的原因很少见(可能只有一个)。这个答案将使用 Visual Basic 术语,语法和上下文。使用的示例来自大量过去的 Stack Overflow 问题。这是为了通过使用帖子中经常出现的各种情况来最大化相关性。还为那些可能需要它的人提供了更多的解释。这里列出了一个类似于你的例子。
注意:
这是 concept-based:没有代码可以粘贴到您的项目中。它旨在帮助您了解导致
NullReferenceException
(NRE)的原因,如何找到它,如何修复它以及如何避免它。 NRE 可以通过多种方式引起,因此这不太可能是您唯一的遭遇。这些示例(来自 Stack Overflow 帖子)并不总是显示出首先做某事的最佳方式。
通常,使用最简单的补救措施。
基本含义
消息“对象未设置为对象的实例”表示您正在尝试使用尚未初始化的对象。这归结为以下之一:
您的代码声明了一个对象变量,但它没有初始化它(创建一个实例或'实例化'它)
你的代码假设的东西会初始化一个对象,但没有
可能,其他代码过早地使仍在使用的对象失效
寻找原因
由于问题是
Nothing
的对象引用,答案是检查它们以找出哪一个。然后确定它未初始化的原因。将鼠标悬停在各种变量上,Visual Studio(VS)将显示其值 - 罪魁祸首将是Nothing
。您还应该从相关代码中删除任何 Try/Catch 块,尤其是 Catch 块中没有任何内容的块。这会导致代码在尝试使用
Nothing
的对象时崩溃。 这就是你想要的,因为它将识别问题的确切位置,并允许你识别导致问题的对象。显示
Error while...
的 Catch 中的MsgBox
将没有多大帮助。此方法也会导致非常糟糕的 Stack Overflow 问题,因为您无法描述实际的异常,涉及的对象甚至代码行。您还可以使用
Locals Window
(Debug - > Windows - > Locals)来检查对象。一旦你知道问题的内容和位置,通常很容易修复并且比发布新问题更快。
也可以看看:
断点
MSDN:如何:使用 Try/Catch 块来捕获异常
MSDN:异常的最佳实践
例子和补救措施
类对象/创建实例
问题是
Dim
不会创建 CashRegister 对象;它只声明一个名为reg
的变量。声明对象变量并创建实例是两回事。补救
声明时,
New
运算符通常可用于创建实例:如果以后只适合创建实例:
注意:**不要在过程中再次使用
Dim
,包括构造函数(Sub New
):这将创建一个局部变量
reg
,该变量仅存在于该上下文(sub)中。模块级别Scope
的reg
变量将在其他地方使用,仍为Nothing
。Visual Basic 尝试使用New重复清除进程:使用New运算符创建一个new对象并调用Sub New - 构造函数 - 您的对象可以在哪里执行任何其他初始化。
需要说明的是,
Dim
(或Private
)只声明一个变量及其Type
。变量的范围 - 无论是整个 module/class 存在还是过程本地 - 取决于它的声明位置。Private | Friend | Public
定义访问级别,而不是 Scope。有关更多信息,请参阅:
新运营商
Visual Basic 中的范围
在 Visual Basic 中访问级别
值类型和引用类型
数组
还必须实例化数组:
此数组仅已声明,未创建。有几种方法可以初始化数组:
注意:从 VS 2010 开始,当使用文字和
Option Infer
初始化本地数组时,As <Type>
和New
元素是可选的:数据类型和数组大小是从分配的数据推断出来的。 Class/Module 级声明仍然需要
As <Type>
和Option Strict
:示例:类对象的数组
数组已创建,但其中的
Foo
对象没有。补救
使用
List(Of T)
将使得没有有效对象的元素变得非常困难:有关更多信息,请参阅:
选项推断声明
Visual Basic 中的范围
Visual Basic 中的数组
列表和集合
.NET 集合(其中有许多变种 - 列表,字典,etc.)也必须实例化或创建。
出于同样的原因,您会得到相同的异常 - 只声明了
myList
,但没有创建实例。补救措施是一样的:常见的疏忽是使用集合
Type
的类:这两个过程都会产生 NRE,因为
barList
只是声明,而不是实例化。创建Foo
的实例也不会创建内部barList
的实例。可能是在构造函数中执行此操作的意图:和以前一样,这是不正确的:
有关更多信息,请参阅List(Of T)班级。
数据提供者对象
使用数据库为 NullReference 提供了许多机会,因为可以有多个对象(
Command
,,Transaction
,Dataset
,DataTable
,DataRows
....)一次使用.**注意:**使用哪个数据提供程序无关紧要 - - MySQL,SQL Server,OleDB 等 - 概念是相同的。例 1
和以前一样,声明了
ds
Dataset 对象,但从未创建过实例。DataAdapter
将填充现有的DataSet
,而不是创建一个DataSet
。在这种情况下,由于ds
是一个局部变量,IDE 会警告您可能会发生这种情况:当声明为 module/class 级变量时,如
con
的情况,编译器无法知道该对象是否是由上游过程创建的。不要忽视警告。补救
例 2
输入错误是一个问题:
Employees
vsEmployee
。没有创建名为“Employee”的DataTable
,因此NullReferenceException
结果试图访问它。另一个潜在的问题是假设当 SQL 包含 WHERE 子句时会有Items
这可能不是这样。补救
由于这使用一个表,因此使用
Tables(0)
将避免拼写错误。检查Rows.Count
也可以帮助:Fill
是一个函数,返回受影响的Rows
的数量,也可以测试:例 3
DataAdapter
将提供TableNames
,如上例所示,但它不解析 SQL 或数据库表中的名称。因此,ds.Tables("TICKET_RESERVATION")
引用 non-existent 表。Remedy是相同的,按索引引用表:
另见DataTable 类。
对象路径/嵌套
代码只测试
Items
,而myFoo
和Bar
也可能都是 Nothing。 补救措施是一次一个地测试整个链或路径:AndAlso
很重要。遇到第一个False
条件后,将不会执行后续测试。这允许代码一次安全地“钻”到 object(s)一个'级别',仅在确定myFoo
(和 if)myFoo
之后评估myFoo.Bar
。编码复杂对象时,对象链或路径可能会很长:无法引用
null
对象的任何“下游”。这也适用于控件:这里,
myWebBrowser
或Document
可以是 Nothing,或者formfld1
元素可能不存在。UI 控件
除此之外,此代码不会预期用户可能没有在一个或多个 UI 控件中选择某些内容。
ListBox1.SelectedItem
可能是Nothing
,因此ListBox1.SelectedItem.ToString
将导致 NRE。补救
在使用之前验证数据(也使用
Option Strict
和 SQL 参数):或者,您可以使用
(ComboBox5.SelectedItem IsNot Nothing) AndAlso...
Visual Basic Forms
这是获得 NRE 的一种相当常见的方式。在 C#中,根据其编码方式,IDE 将报告当前上下文中不存在
Controls
,或“无法引用 non-static 成员”。所以,在某种程度上,这是一个 VB-only 的情况。它也很复杂,因为它可能导致级联故障。无法以这种方式初始化数组和集合。此初始化代码将在构造函数创建
Form
或Controls
之前运行。结果是:列表和集合将是空
该数组将包含 Nothing 的五个元素
somevar
赋值将导致立即 NRE,因为 Nothing 没有.Text
属性稍后引用数组元素将导致 NRE。如果在
Form_Load
中执行此操作,由于奇怪的错误,IDE 可能不会在发生异常时报告该异常。当您的代码尝试使用该数组时,将弹出异常。这个“沉默的例外”是在这篇文章中详述。出于我们的目的,关键是当创建表单(Sub New
或Form Load
事件)时发生灾难性事件时,异常可能未被报告,代码退出过程并只显示表单。由于您的
Sub New
或Form Load
事件中没有其他代码将在 NRE 之后运行,因此许多其他内容可以保持未初始化状态。注意这适用于任何和所有控制和组件引用,使这些非法在以下情况:
部分补救措施
很奇怪 VB 没有提供警告,但补救措施是在表单级别声明容器,但当控件确实存在时,在表单加载事件处理程序中初始化。只要代码在
InitializeComponent
调用之后,就可以在Sub New
中完成:阵列代码可能还没有走出困境。在
Me.Controls
中找不到容器控件中的任何控件(如GroupBox
或Panel
);它们将位于该 Panel 或 GroupBox 的 Controls 集合中。当控件名拼写错误时("TeStBox2"
),也不会返回控件。在这种情况下,Nothing
将再次存储在那些数组元素中,并且当您尝试引用它时将产生 NRE。现在你知道你在寻找什么,这些应该很容易找到:

“Button2”位于
Panel
补救
而不是使用表单的
Controls
集合按名称间接引用,使用控件引用:功能无效
这种情况下,IDE 会警告您“并非所有路径都返回值,并且可能会导致
NullReferenceException
”。您可以通过将Exit Function
替换为Return Nothing
来取消警告,但这不能解决问题。在someCondition = False
时尝试使用返回的任何内容都会导致 NRE:补救
用
Return bList
替换函数中的Exit Function
。返回空List
与返回Nothing
不同。如果返回的对象有可能Nothing
,请在使用之前进行测试:执行不力 Try/Catch
糟糕实施的 Try/Catch 可以隐藏问题所在并导致新问题:
这是一个未按预期创建对象的情况,但也演示了空
Catch
的计数器有用性。SQL 中有一个额外的逗号(在'mailaddress'之后),这会在
.ExecuteReader
处产生异常。在Catch
什么都不做之后,Finally
尝试执行清理,但由于你不能Close
一个 nullDataReader
对象,一个全新的NullReferenceException
结果。一个空的
Catch
区块是魔鬼的操场。这个 OP 感到困惑,为什么他在Finally
区块获得 NRE。在其他情况下,一个空的Catch
可能会导致更多下游的其他东西变得混乱,并导致您花时间在错误的地方查找错误的问题。 (上面描述的“沉默的例外”提供相同的娱乐 value.)补救
不要使用空的 Try/Catch 块 - 让代码崩溃,以便 a)确定原因 b)识别位置和 c)应用适当的补救措施。 Try/Catch 块不是为了隐藏来自唯一有资格修复它们的人的例外 - 开发人员。
DBNull 与 Nothing 不同
IsDBNull
函数用于测试值是否等于System.DBNull
:来自 MSDN:补救
和以前一样,您可以测试 Nothing,然后测试特定值:
例 2
FirstOrDefault
返回第一个项目或默认值,对于引用类型是Nothing
而从不DBNull
:控制
如果找不到带有
chkName
的CheckBox
(或存在于GroupBox
中),则chk
将为 Nothing,并且尝试引用任何属性将导致异常。补救
DataGridView
DGV 有一些周期性的怪癖:
如果
dgvBooks
有AutoGenerateColumns = True
,它将创建列,但它没有命名它们,因此上面的代码在按名称引用时失败。补救
手动命名列,或按索引引用:
示例 2 - 注意 NewRow
当
DataGridView
具有AllowUserToAddRows
为True
(默认值)时,底部 blank/new 行中的Cells
将包含Nothing
。大多数使用内容的尝试(例如,ToString
)将导致 NRE。补救
使用
For/Each
循环并测试IsNewRow
属性以确定它是否是最后一行。无论AllowUserToAddRows
是否为真,这都有效:如果使用
For n
循环,请修改行计数或在IsNewRow
为真时使用Exit For
。My.Settings(StringCollection)
在某些情况下,尝试使用
My.Settings
中的项目StringCollection
可能会在您第一次使用它时导致 NullReference。解决方案是相同的,但不是那么明显。考虑:由于 VB 正在为您管理设置,因此期望它初始化集合是合理的。它将会,但前提是您之前已在集合中添加了一个初始条目(在“设置”编辑器中)。由于在添加项目时(显然)初始化了集合,因此当设置编辑器中没有要添加的项目时,它仍然是
Nothing
。补救
在表单的
Load
事件处理程序中初始化设置集合,需要 if/when:通常,只需在应用程序第一次运行时初始化
Settings
集合。另一种方法是在项目 - >设置|中为集合添加初始值 FooBars,保存项目,然后删除假值。关键点
您可能忘记了
New
运算符。要么
你假设的东西可以完美无瑕地将初始化对象返回到你的代码,但事实并非如此。
不要忽略编译器警告(永远)并使用
Option Strict On
(总是)。MSDN NullReference 异常
另一种情况是将空对象转换为值类型。例如,下面的代码:
它将在演员表上抛出
NullReferenceException
。在上面的示例中似乎很明显,但是这可能发生在更多“late-binding”错综复杂的场景中,其中 null 对象已从您不拥有的某些代码返回,并且转换例如由某个自动系统生成。这方面的一个例子是这个带有 Calendar 控件的简单 ASP.NET 绑定片段:
这里,
SelectedDate
实际上是Calendar
Web 控件类型的DateTime
类型的属性,并且绑定可以完美地返回 null。隐式 ASP.NET Generator 将创建一段代码,它将等同于上面的强制转换代码。这会引发一个很难发现的NullReferenceException
,因为它位于 ASP.NET 生成的代码中,编译得很好......这意味着有问题的变量没有任何意义。我可以这样生成:
这将抛出错误,因为当我声明变量“
connection
”时,它没有指向任何东西。当我尝试调用成员“Open
”时,没有引用它来解决它,它将抛出错误。要避免此错误:
在尝试对它们执行任何操作之前始终初始化对象。
如果您不确定对象是否为 null,请使用
object == null
进行检查。JetBrains 的 Resharper 工具将识别代码中可能存在空引用错误的每个位置,允许您进行空检查。这个错误是错误的头号来源,恕我直言。
这意味着您的代码使用了一个设置为 null 的对象引用变量(i.e.它没有引用实际的对象实例)。
为了防止错误,可以在使用之前测试 null 为 null 的对象。
请注意,无论情况如何,.NET 中的原因始终相同:
您从未向变量分配内容,从未创建分配给变量的值的实例,或者您手动将变量设置为
Nothing
/null
,或者您调用了一个将变量设置为Nothing
/null
的函数。抛出此异常的一个示例是:当您尝试检查某些内容时,该值为 null。
例如:
当您尝试对尚未实例化的事物 i.e 执行操作时,.NET 运行时将抛出 NullReferenceException。上面的代码。
与 ArgumentNullException 相比,如果方法期望传递给它的内容不为 null,则通常将其作为防御措施抛出。
更多信息在C#NullReferenceException 和 Null 参数中。
更新 C#8.0,2019:可以为空的引用类型
C#8.0 引入了可空引用类型和non-nullable 引用类型。因此,必须只检查可空引用类型以避免NullReferenceException。
如果尚未初始化引用类型,并且想要设置或读取其中一个属性,则会抛出NullReferenceException。
例:
您可以通过检查变量是否为空来简单地避免这种情况:
要完全理解为什么抛出 NullReferenceException,重要的是要知道价值类型和[492]之间的区别[493]。
所以,如果你正在处理值类型,那么 NullReferenceExceptions 就不会发生。虽然在处理参考类型时需要保持警惕!
正如名称所暗示的,只有引用类型可以保存引用或从字面上指向任何内容(或“null”)。值类型始终包含值。
参考类型(必须检查这些类型):
动态
宾语
串
值类型(您可以忽略这些):
数字类型
积分类型
Floating-point 类型
十进制
布尔
用户定义的结构
NullReferenceExceptions
可能发生的另一种情况是(错误地)使用作为运营商:这里,
Book
和Car
是不兼容的类型; aCar
不能 converted/cast 到Book
。当此强制转换失败时,as
返回null
。在此之后使用mybook
会导致NullReferenceException
。通常,您应该使用强制转换或
as
,如下所示:如果您希望类型转换始终成功(即您知道对象应该提前是什么),那么您应该使用强制转换:
如果您不确定类型,但想要尝试将其用作特定类型,请使用
as
:您正在使用包含空值引用的对象。所以它给出了一个 null 异常。在示例中,字符串值为 null,并且在检查其长度时,发生了异常。
例:
异常错误是:
System.NullReferenceException:对象引用未设置为对象的实例。在 Program.Main()
虽然导致NullReferenceException 异常和 avoid/fix 这种异常的方法已经在其他答案中得到解决,但许多程序员还没有学到的是如何在开发过程中独立调试这些异常。
在 Visual Studio 中,由于Visual Studio 调试器,这通常很容易。
首先,确保将捕获正确的错误 - 请参阅如何在 VS2010 中允许打破“System.NullReferenceException”?注 1
然后是从调试开始(F5)或将[VS Debugger]附加到 Running Process。有时使用Debugger.Break可能会有用,它会提示启动调试器。
现在,当抛出(或未处理)NullReferenceException 时,调试器将在发生异常的行上停止(记住上面的规则集?)。有时错误很容易发现。
例如,在以下行中,唯一可以导致异常的代码是
myString
求值为 null。这可以通过查看即时窗口中的观察窗口或运行表达式来验证。在更高级的情况下,例如,您需要使用上述技术之一(Watch 或 Immediate Windows)来检查表达式,以确定
str1
是否为 null 或str2
是否为空。一旦找到抛出异常的位置,通常会向后推理以找出空值[538]的位置 - 通常是微不足道的 -
花些时间了解异常原因。检查空表达式。检查可能导致此类空表达式的先前表达式。添加断点并根据需要逐步执行该程序。使用调试器。
1 如果 Break of Throws 过于激进且调试器在.NET 或 3rd-party 库中的 NPE 上停止,则休息 User-Unhandled可用于限制捕获的异常。另外,VS2012 引入了只是我的代码,我也建议启用。
从
object
(或从System.ValueType
或System.Enum
类之一,或从接口类型)到值类型(除Nullable<>
之外)的拆箱转换(强制转换)本身给出了NullReferenceException
。在另一个方向上,从
Nullable<>
(HasValue
等于false
)到引用类型的拳击转换可以给出null
引用,然后可以导致NullReferenceException
。经典的例子是:有时拳击以另一种方式发生。例如,使用此 non-generic 扩展方法:
以下代码将有问题:
出现这些情况是因为运行时在装箱
Nullable<>
实例时使用的特殊规则。当实体框架中使用的实体的类名与 Web 表单 code-behind 文件的类名相同时添加一个案例。
假设您有一个 Web 表单 Contact.aspx,其代码隐藏类是 Contact,并且您有一个实体名称 Contact。
然后下面的代码将在您调用 context.SaveChanges()时抛出 NullReferenceException
为了完整性 DataContext 类
和联系实体类。有时实体类是部分类,因此您也可以在其他文件中扩展它们。
当实体和代码隐藏类都在同一名称空间中时,会发生错误。要解决此问题,请为 Contact.aspx 重命名实体类或代码隐藏类。
原因我仍然不确定原因。但只要任何实体类都会扩展 System.Web.UI.Page,就会发生这种错误。
有待讨论,请看DbContext.saveChanges()中的 NullReferenceException
另一个可能会收到此异常的一般情况涉及在单元测试期间模拟类。无论使用何种模拟框架,都必须确保正确模拟类层次结构的所有适当级别。特别是,必须模拟被测试代码引用的
HttpContext
的所有属性。有关详细示例,请参阅“测试自定义 AuthorizationAttribute 时抛出 NullReferenceException”。
我对回答这个问题有不同的看法。这种答案**“我还能做些什么来避免它?**”
当跨越不同层工作时,例如在 MVC 应用程序中,控制器需要服务来调用业务操作.在这种情况下,依赖注入容器可用于初始化服务以避免NullReferenceException**。这意味着您不必担心检查 null 并且只需从控制器调用服务,就好像它们总是可用(并初始化)为单例或原型。
关于**“我应该怎么做”的问题**,可以有很多答案。
在开发过程中防止此类错误情况的更“正式”方法是在代码中应用按合同设计。这意味着您需要在开发时在系统上设置类不变量,and/or 甚至 function/method 前置条件和后置条件。
简而言之,类不变量确保您的类中存在一些在正常使用中不会被违反的约束(因此,类不会处于不一致状态)。前置条件意味着作为 function/method 的输入给出的数据必须遵循一些约束集,并且**永远不会违反它们,并且后置条件意味着 function/method 输出必须再次遵循设置约束而不会违反它们。在执行 bug-free 程序期间,合同条件应该永远不会被违反,因此在调试模式下实际检查合同设计,同时在发布中禁用合同条件,以最大化已开发的系统性能。
这样,您可以避免违反约束集的
NullReferenceException
个案例。例如,如果在类中使用对象属性X
,稍后尝试调用其中一个方法并且X
具有空值,则会导致NullReferenceException
:但是如果你设置“属性 X 必须永远不会有空值”作为方法前置条件,那么你可以阻止前面描述的场景:
对于这个原因,.NET 项目存在于.NET 应用程序中。
或者,可以使用断言来应用合同设计。
**更新:**值得一提的是,这个词是由 Bertrand Meyer 创造的与他设计的 Eiffel 编程语言有关。
当我们尝试访问 null 对象的 Properties 或者字符串值变为空并且我们尝试访问字符串方法时,抛出
NullReferenceException
。例如:
**TL; DR:**尝试使用
Html.Partial
代替Renderpage
当我尝试通过发送一个模型在视图中呈现视图时,我得到
Object reference not set to an instance of an object
,如下所示:调试显示模型在 MyOtherView 中是空的。直到我将其更改为:
它奏效了。
此外,我没有
Html.Partial
开始的原因是因为 Visual Studio 有时在Html.Partial
下抛出 error-looking 波浪线如果它在一个不同构造的foreach
循环中,即使它不是真的错误:但是我能够运行应用程序而没有出现这个“错误”的问题。我能够通过改变
foreach
循环的结构来消除错误,如下所示:虽然我有一种感觉,因为 Visual Studio 误读了&符号和括号。
你能为这个做什么?
这里有很多很好的答案,解释了什么是空引用以及如何调试它。但是如何防止这个问题或者至少让它更容易被捕获的问题很少。
检查参数
例如,方法可以检查不同的参数以查看它们是否为 null 并抛出
ArgumentNullException
,这是一个明显为此目的而创建的异常。ArgumentNullException
的构造函数甚至将参数的名称和消息作为参数,因此您可以准确地告诉开发人员问题是什么。使用工具
还有几个图书馆可以提供帮助。例如,“Resharper”可以在编写代码时为您提供警告,尤其是在您使用其属性时:NotNullAttribute
有“Microsoft Code Contracts”,你使用像
Contract.Requires(obj != null)
这样的语法,它为你提供运行时和编译检查:介绍代码合同。还有“PostSharp”,它允许你只使用这样的属性:
通过这样做并使构建过程的 PostSharp 部分
obj
将在运行时检查为 null。见:PostSharp null 检查普通代码解决方案
或者,您始终可以使用普通旧代码编写自己的方法。例如,这是一个可用于捕获空引用的结构。它的模型与
Nullable<T>
相同:您将使用与使用
Nullable<T>
相同的方式,除非目标是完全相反 - 不允许null
。这里有些例子:NotNull<T>
被隐式地转换为T
,因此您可以在任何需要它的地方使用它。例如,您可以将Person
对象传递给采用NotNull<Person>
的方法:正如您在上面看到的那样,您可以通过
Value
属性访问基础值。或者,您可以使用显式或隐式强制转换,您可以看到一个带有返回值的示例:或者你甚至可以在方法通过执行转换返回
T
(在这种情况下为Person
)时使用它。例如,以下代码就像上面的代码:结合扩展
将
NotNull<T>
与扩展方法结合使用可以涵盖更多情况。以下是扩展方法的示例:以下是如何使用它的示例:
GitHub 上
为了您的参考,我在 GitHub 上提供了上面的代码,您可以在以下位置找到它:
https://github.com/luisperezphd/NotNull
相关语言功能
C#6.0 引入了“null-conditional 运算符”,对此有所帮助。使用此功能,您可以引用嵌套对象,如果其中任何一个是
null
,则整个表达式返回null
。这减少了在某些情况下您必须执行的空检查的数量。语法是在每个点之前加上一个问号。以下面的代码为例:
想象一下,
country
是Country
类型的对象,它具有一个名为State
的属性,依此类推。如果country
,State
,County
或City
是null
则address will be
null. Therefore you only have to check whether
地址is
null`。这是一个很棒的功能,但它可以为您提供更少的信息。它并没有明确表明 4 中的哪一个是空的。
Built-in 喜欢 Nullable?
C#有一个很好的
Nullable<T>
简写,你可以通过在类型之后加一个问号来使某些东西可以为空,如此int?
。如果 C#具有类似上面的
NotNull<T>
结构并且有类似的简写,可能是感叹号(!)以便您可以编写类似于:public void WriteName(Person! person)
的内容,这将是很好的。您可以使用 c#6 中的 Null-conditional 运算符以干净的方式修复 NullReferenceException,并编写更少的代码来处理空检查。
它用于在执行成员访问(?.)或索引(?[)操作之前测试 null。
例
相当于:
结果是当 p 为 null 或 p.Spouse 为 null 时,名称将为 null。
否则,将为变量名称分配 p.Spouse.FirstName 的值。
详情请见:Null-conditional 运营商
错误行“对象引用未设置为对象的实例。”表示您尚未将实例对象分配给对象引用,但仍然访问该对象的 properies/methods。
例如:假设你有一个名为 myClass 的类,它包含一个属性 prop1。
现在您正在访问其他类中的 prop1,如下所示:
上面的行抛出错误,因为类 myClass 的引用已声明但未实例化,或者对象的实例未分配给该类的引用。
要修复此问题,您必须实例化(将对象分配给该类的引用)。
有趣的是,这个页面上没有一个答案提到了两个边缘情况,希望没有人介意我添加它们:
边缘情况#1:并发访问字典
.NET 中的通用字典不是 thread-safe,当您尝试从两个并发线程访问密钥时,它们有时可能会抛出
NullReference
甚至(更频繁)KeyNotFoundException
。在这种情况下,例外是非常误导的。边缘情况#2:不安全的代码
如果
unsafe
代码抛出NullReferenceException
,您可能会查看指针变量,并检查它们是否为IntPtr.Zero
等。这是相同的事情(“空指针异常”),但在不安全的代码中,变量通常被强制转换为 value-types/arrays 等,并且你猛烈地撞墙,想知道 value-type 如何抛出这个异常。(顺便说一句,除非你需要,否则 non-using 不安全代码的另一个原因)
当您尝试使用的类的对象未实例化时,会发生未设置为对象实例的 NullReferenceException 或 Object 引用。例如:
假设您有一个名为 Student 的类。
现在,考虑另一个你试图检索学生全名的课程。
如上面的代码所示,语句Student s - 只声明 Student 类型的变量,注意此时没有实例化 Student 类。因此,当语句**s.GetFullName()**被执行时,它将抛出 NullReferenceException。
好吧,简单来说:
您正在尝试访问未创建或当前不在内存中的对象。
那么如何解决这个问题:
调试并让调试器中断...它将直接将您带到已损坏的变量...现在您的任务是简单地修复此问题。在适当的位置使用new关键字。
如果它是由某些数据库命令引起的,因为该对象不存在,那么您需要做的就是进行空检查并处理它:
通过名称查找我的意思是一些框架允许你使用字符串 FIndObjects,代码可能如下所示:FindObject(“ObjectName”);
如果我们考虑可以抛出此异常的常见方案,则在顶部访问具有对象的属性。
例如:
在这里,如果 address 为 null,那么你将得到 NullReferenceException。
所以,作为一种实践,我们应该在访问这些对象中的属性之前始终使用 null 检查(特别是在泛型中)
从字面上看,修复 NullReferenceExeption 最简单的方法有两种。如果您有一个附加脚本的 GameObject 和一个名为 rb(rigidbody)的变量,那么当您开始游戏时,此变量将从 null 开始。
这就是您获得 NullReferenceExeption 的原因,因为计算机没有存储在该变量中的数据。
我将使用 RigidBody 变量作为示例。
我们可以通过以下几种方式实际轻松添加数据:
使用 AddComponent> Physics> Rigidbody 将 RigidBody 添加到对象
然后进入你的脚本并输入
rb = GetComponent<Rigidbody>();
这行代码最适合您的
Start()
或Awake()
函数。您可以以编程方式添加组件,并使用一行代码同时分配变量:
rb = AddComponent<RigidBody>();
进一步说明:如果您希望统一向对象添加组件而您可能忘记添加组件,则可以在类声明上方输入
[RequireComponent(typeof(RigidBody))]
(所有用法下方的空间)。享受并享受制作游戏的乐趣!
如果在保存或编译构建期间收到此消息,只需关闭所有文件,然后打开任何文件进行编译和保存。
对我来说,原因是我重命名了文件,旧文件仍然打开。
要使用对象的方法和成员,首先必须创建该对象。如果你没有创建它(应该保存对象的变量没有被初始化),但是你尝试使用它的方法或变量,你将得到那个错误。
有时你可能忘了做初始化。
编辑: new 不能返回 null,但是失败时 fire 会异常。很久以前,某些语言就是这种情况,但现在已经不是了。 Thanks @John Saunders 用于指出。
这基本上是一个空引用异常。作为微软州 -
那是什么意思?
这意味着如果任何没有任何价值的成员并且我们正在让该成员执行某项任务,那么系统无疑会抛出一条消息并说 -
“嘿等等,那个成员没有价值观,所以它无法完成你交给它的任务。”
异常本身表示正在引用某些内容但其值未设置。所以这表示它只在使用引用类型时发生,因为值类型是 non-nullable。
如果我们使用 Value 类型成员,则不会发生 NullReferenceException。
上面的代码显示了简单的字符串,该字符串分配有null值。
现在,当我尝试打印字符串str的长度时,我确实得到了一个未处理的类型'System.NullReferenceException'的异常消息,因为成员str指向 null 并且不能有任何长度为 null。
当我们忘记实例化引用类型时,也会发生'NullReferenceException'。
假设我有一个类和成员方法。我没有实例化我的课程,只是命名我的班级。现在,如果我尝试使用该方法,编译器将抛出错误或发出警告(取决于编译器)。
上面代码的编译器引发了一个错误,即变量obj未被赋值,这表示我们的变量具有空值或什么都没有。上面代码的编译器引发了一个错误,即变量obj未被赋值,这表示我们的变量具有空值或什么都没有。
为什么会这样?
NullReferenceException 是由于我们没有检查对象值的错误而产生的。我们经常在代码开发中保留未选中的对象值。
当我们忘记实例化对象时也会出现这种情况。使用可返回或设置空值的方法,属性,集合等也可能是此异常的原因。
怎么可以避免?
有许多方法和方法可以避免这个着名的例外:
显式检查:我们应该遵循检查对象,属性,方法,数组和集合的传统,无论它们是否为空。这可以使用像 if-else if-else 等条件语句简单地实现。
异常处理:管理此异常的重要方法之一。使用简单的 try-catch-finally 块,我们可以控制此异常并维护它的日志。当您的应用程序处于生产阶段时,这非常有用。
空值运算符:Null Coalescing 运算符和空条件运算符也可以在将值设置为对象,变量,属性和字段时使用。
调试器:对于开发人员,我们拥有调试的重要武器。如果我们在开发面部遇到 NullReferenceException,我们可以使用调试器来获取异常的来源。
In-built 方法:系统方法(如 GetValueOrDefault(),IsNullOrWhiteSpace()和 IsNullorEmpty())检查空值,如果存在空值,则指定默认值。
这里有很多好的答案。您还可以通过我的博客上的示例查看更详细的说明。
希望这也有帮助!
有一种情况可能发生类相关。在我陈述决议之前,这个问题最终被关闭了:https://stackoverflow.com/questions/43348009/unable-to-instantiate-class
**注意没有实例化的类:**如果类中构造函数的任何部分抛出
null reference exception
,则该类不会实例化。在我的情况下,它试图从 web.config 获得一个不存在的连接字符串。我实例化了一个类:
在类本身内部是一个从
web.config
获取连接字符串的调用。构造函数的这一部分抛出了一个空值异常,因此myClass
为 null。如果你有一个没有实例化的类的情况,请尝试确保类构造函数的任何部分都没有抛出
null value exception
。 F-11 并逐步完成该类并确保没有空值。