web-dev-qa-ko.com

NullReferenceException은 무엇이며 어떻게 수정합니까?

몇 가지 코드가 있고 실행될 때 NullReferenceException이 throw됩니다.

개체 참조가 개체의 인스턴스로 설정되지 않았습니다.

이것이 의미하는 바는 무엇이며,이 오류를 해결하기 위해 무엇을 할 수 있습니까?

1877
John Saunders

원인이 무엇입니까?

결론

null (또는 VB.NET의 Nothing)을 사용하려고합니다. 즉, null로 설정했거나 전혀 설정하지 않았습니다.

다른 것과 마찬가지로 null이 전달됩니다. null in 메소드 "A"인 경우 "B"메소드가 null to 메소드 "A"를 전달했을 수 있습니다. .

null은 다른 의미를 가질 수 있습니다.

  1. 초기화되지 않음 인 객체 변수이므로 아무것도 가리지 않습니다.이 경우 이러한 객체의 속성이나 메서드에 액세스하면 NullReferenceException이 발생합니다.
  2. 개발자는 사용할 수있는 의미있는 값이 없음을 나타 내기 위해 의도적으로 null을 사용합니다. C #에는 변수에 대한 nullable 데이터 유형 개념이 있습니다 (데이터베이스 테이블에 nullable 필드가있을 수 있음). null을 여기에 할당 할 수 있습니다. int? a = null;와 같이 값이 저장되어 있지 않음을 나타냅니다. 여기서 물음표는 변수 a에 null을 저장할 수 있음을 나타냅니다. if (a.HasValue) {...} 또는 if (a==null) {...}을 사용하여 확인할 수 있습니다. 이 예제의 a과 같은 널 입력 가능 변수는 a.Value를 통해 명시 적으로 또는 a을 통해 정상적으로 액세스 할 수 있습니다.
    NoteInvalidOperationExceptionNullReferenceException 인 경우 a.Value를 통해 액세스하면 a 대신 null이 (가) 발생합니다. 사전에 확인해야합니다. 즉, 다른 nullable 변수가있는 경우 int b; 그런 다음 if (a.HasValue) { b = a.Value; } 또는 더 짧은 if (a != null) { b = a; }과 같은 과제를 수행해야합니다.

이 기사의 나머지 부분은 더 자세하게 설명하고 많은 프로그래머가 종종 NullReferenceException을 일으킬 수있는 실수를 보여줍니다.

더 구체적으로

NullReferenceExceptionalways를 던지는 런타임은 같은 것을 의미합니다. 참조를 사용하려고하고 있고 참조가 초기화되지 않았습니다 (또는 once 초기화되었습니다. 이지만 no longer initialized)입니다.

이는 참조가 null이고 null 참조를 통해 멤버 (예 : 메소드)에 액세스 할 수 없음을 의미합니다. 가장 간단한 경우 :

string foo = null;
foo.ToUpper();

NullReferenceException을 가리키는 string 참조에서 인스턴스 메소드 ToUpper()을 호출 할 수 없기 때문에 두 번째 줄에 null이 발생합니다.

디버깅

NullReferenceException의 출처는 어떻게 찾습니까? 예외가 발생하는 위치에서 정확하게 발생하는 예외 자체를 보는 것 외에도 Visual Studio에서 디버깅하는 일반적인 규칙은 다음과 같습니다. 전략적 중단 점 배치 및 변수 검사 (Quick) Watch 창을 열거 나 Locals 및 Autos와 같은 다양한 디버깅 패널을 사용하여 이름 위에.

참조가 설정되었거나 설정되지 않은 위치를 찾으려면 해당 이름을 마우스 오른쪽 단추로 클릭하고 "모든 참조 찾기"를 선택하십시오. 그런 다음 발견 된 모든 위치에 중단 점을 배치하고 디버거가 연결된 상태에서 프로그램을 실행할 수 있습니다. 디버거가 이러한 중단 점에서 중단 될 때마다 참조가 널이 아닌 것으로 예상되는지 여부를 판별하고 변수를 검사하여 예상시 인스턴스를 가리키는 지 확인해야합니다.

이 방법으로 프로그램 흐름을 따르면 인스턴스가 null이 아니어야하는 위치와 제대로 설정되지 않은 이유를 찾을 수 있습니다.

예외가 발생할 수있는 몇 가지 일반적인 시나리오 :

일반적인

ref1.ref2.ref3.member

Ref1 또는 ref2 또는 ref3이 null이면 NullReferenceException을 얻게됩니다. 문제를 해결하려면 식을 더 간단한 것으로 다시 작성하여 널 중 하나를 찾으십시오.

var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member

특히 HttpContext.Current.User.Identity.Name에서 HttpContext.Current은 null이거나 User 속성은 null이거나 Identity 속성은 null 일 수 있습니다.

간접

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.
    }
}

하위 (Person) 널 참조를 피하려면 상위 (Book) 오브젝트의 생성자에서이를 초기화 할 수 있습니다.

중첩 객체 이니셜 라이저

중첩 객체 이니셜 라이저에도 동일하게 적용됩니다.

Book b1 = new Book { Author = { Age = 45 } };

이것은

Book b1 = new Book();
b1.Author.Age = 45;

new 키워드가 사용되는 동안 Book의 새 인스턴스 만 작성하고 Person의 새 인스턴스는 작성하지 않으므로 Author 특성은 여전히 ​​null입니다.

중첩 컬렉션 이니셜 라이저

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 PersonPerson의 인스턴스 만 생성하지만 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.

수집/목록/사전

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.

행사

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 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 속성을 참조 할 때 예외가 발생하면 뷰를 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 -->

WPF 컨트롤 생성 순서 및 이벤트

WPF 컨트롤은 비주얼 트리에 나타나는 순서대로 InitializeComponent을 호출하는 동안 만들어집니다. 늦게 생성 된 컨트롤을 참조하는 NullReferenceException 동안 발생하는 이벤트 핸들러 등을 사용하여 초기에 생성 된 컨트롤의 경우 InitializeComponent이 발생합니다.

예를 들면 다음과 같습니다.

<Grid>
    <!-- Combobox declared first -->
    <ComboBox Name="comboBox1" 
              Margin="10"
              SelectedIndex="0" 
              SelectionChanged="comboBox1_SelectionChanged">
        <ComboBoxItem Content="Item 1" />
        <ComboBoxItem Content="Item 2" />
        <ComboBoxItem Content="Item 3" />
    </ComboBox>

    <!-- Label declared later -->
    <Label Name="label1" 
           Content="Label"
           Margin="10" />
</Grid>

comboBox1 전에 label1이 (가) 생성됩니다. comboBox1_SelectionChanged가`label1을 참조하려고 시도하면 아직 작성되지 않은 것입니다.

private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    label1.Content = comboBox1.SelectedIndex.ToString(); // NullReference here!!
}

XAML에서 선언의 순서를 변경하면 (즉, 디자인 철학의 문제를 무시하고 label1 앞에 comboBox1 나열] 최소한 NullReferenceException이 (가) 해결됩니다.

as으로 캐스트

var myThing = someObject as Thing;

이것은 캐스트가 실패 할 때 (그리고 someObject 자체가 null 일 때) InvalidCastException을 발생시키지 않지만 null을 반환합니다. 그러니 알아 두십시오.

LINQ FirstOrDefault () 및 SingleOrDefault ()

일반 버전 First()Single()은 아무것도없는 경우 예외를 발생시킵니다. 이 경우 "OrDefault"버전은 null을 반환합니다. 그러니 알아 두십시오.

각각

null 수집을 반복하려고하면 foreach이 발생합니다. 일반적으로 컬렉션을 반환하는 메서드의 예기치 않은 null 결과로 인해 발생합니다.

 List<int> list = null;    
 foreach(var v in list) { } // exception

보다 현실적인 예-XML 문서에서 노드를 선택하십시오. 노드를 찾을 수 없지만 초기 디버깅에서 모든 속성이 유효한 것으로 표시되면 throw됩니다.

 foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))

피하는 방법

null을 명시 적으로 확인하고 null 값을 무시하십시오.

참조가 때때로 null 인 것으로 예상되는 경우 인스턴스 멤버에 액세스하기 전에 null인지 확인할 수 있습니다.

void PrintName(Person p) {
    if (p != null) {
        Console.WriteLine(p.Name);
    }
}

null을 명시 적으로 확인하고 기본값을 제공하십시오.

예를 들어, 찾고있는 객체를 찾을 수없는 경우 인스턴스를 반환 할 것으로 예상되는 메서드 호출은 null을 반환 할 수 있습니다. 이 경우 기본값을 반환하도록 선택할 수 있습니다.

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;
}

값이 null이되어서는 안되는 경우 Debug.Assert를 사용하여 예외가 발생하기 전에 문제를 조기에 포착하십시오.

개발 중에 메소드가 null을 리턴 할 수는 있지만 리턴해서는 안된다는 것을 알면 Debug.Assert()을 사용하여 가능한 빨리 중단 할 수 있습니다.

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.
}

이 검사 릴리스 빌드에서는 끝나지 않음 이지만 릴리스 모드에서 런타임에 book == null 일 때 NullReferenceException을 다시 발생시킵니다.

null 인 경우 기본값을 제공하려면 널 입력 가능 값 유형에 GetValueOrDefault()을 사용하십시오.

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;
}

배열에 대해 ?. 또는 ?[x] null 조건 연산자를 사용하십시오 (C # 6 및 VB.NET 14에서 사용 가능).

이것을 안전한 네비게이션 또는 엘비스 (모양 후) 연산자라고도합니다. 연산자의 왼쪽에있는 표현식이 널이면 오른쪽이 평가되지 않고 대신 널이 리턴됩니다. 이는 다음과 같은 경우를 의미합니다.

var title = person.Title.ToUpper();

사람에게 제목이 없으면 null 값을 가진 속성에서 ToUpper을 호출하려고하기 때문에 예외가 발생합니다.

C # 5 이하에서는 다음과 같이 보호 할 수 있습니다.

var title = person.Title == null ? null : person.Title.ToUpper();

이제 제목 변수는 예외를 던지는 대신 null이됩니다. C # 6은 이에 대한 짧은 구문을 소개합니다.

var title = person.Title?.ToUpper();

결과적으로 제목 변수는 null이되고 person.TitleToUpper이면 null에 대한 호출이 수행되지 않습니다.

물론, still 널을 title 확인하거나 널 병합 연산자 (??)와 함께 널 조건 연산자를 사용하여 기본값을 제공해야합니다.

// 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를 반환합니다. 요소.

Null 컨텍스트 사용 (C # 8에서 사용 가능) :

C # 8에 도입 된 null 컨텍스트 및 nullable 참조 유형은 변수에 대한 정적 분석을 수행하고 값이 null이거나 잠재적으로 null로 설정된 경우 컴파일러 경고를 제공합니다. 널 입력 가능 참조 유형을 사용하면 유형을 명시 적으로 널로 허용 할 수 있습니다.

Csproj 파일의 Nullable 요소를 사용하여 프로젝트에 대해 Null 허용 주석 컨텍스트 및 Null 허용 경고 컨텍스트를 설정할 수 있습니다. 이 요소는 컴파일러가 형식의 null 허용 여부를 해석하는 방법과 생성되는 경고를 구성합니다. 유효한 설정은 다음과 같습니다.

  • enable : 널 입력 가능 주석 컨텍스트가 사용 가능합니다. 널 입력 가능 경고 컨텍스트가 사용 가능합니다. 예를 들어 문자열과 같은 참조 유형의 변수는 널 입력이 불가능합니다. 모든 Null 허용 경고가 활성화되었습니다.
  • disable : 널 입력 가능 주석 컨텍스트가 사용 불가능합니다. 널 입력 가능 경고 컨텍스트가 사용 불가능합니다. C #의 이전 버전과 마찬가지로 참조 유형의 변수는 분명하지 않습니다. 모든 Null 허용 경고가 비활성화되었습니다.
  • safeonly : 널 입력 가능 주석 컨텍스트가 사용 가능합니다. 널 입력 가능 경고 컨텍스트는 안전합니다. 참조 유형의 변수는 널 입력이 불가능합니다. 모든 안전 무효 성 경고가 활성화되었습니다.
  • 경고 : 널 입력 가능 주석 컨텍스트가 사용 불가능합니다. 널 입력 가능 경고 컨텍스트가 사용 가능합니다. 참조 유형의 변수는 분명하지 않습니다. 모든 Null 허용 경고가 활성화되었습니다.
  • safeonlywarnings : 널 입력 가능 주석 컨텍스트가 사용 불가능합니다. 널 입력 가능 경고 컨텍스트는 안전합니다. 참조 유형의 변수는 분명하지 않습니다. 모든 안전 무효 성 경고가 활성화되었습니다.

널 입력 가능 참조 유형은 널 입력 가능 값 유형과 동일한 구문을 사용하여 나타납니다. ?가 변수 유형에 추가됩니다.

반복자에서 널 참조를 디버깅하고 수정하기위한 특수 기술

C #은 "반복자 블록"(일부 인기있는 다른 언어에서는 "생성기"라고 함)을 지원합니다. Null 역 참조 예외는 지연된 실행으로 인해 반복기 블록에서 디버그하기가 특히 까다로울 수 있습니다.

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();
}

왜 이것이 잘못 되었습니까? 반복자 블록은 foreach!까지 실제로 run 이 아니기 때문에! GetFrobs에 대한 호출은 단순히 iterated 반복자 블록을 실행할 개체를 반환합니다.

이와 같이 null 검사를 작성하면 null 역 참조를 방지 할 수 있지만 null 인수 예외를 iteration 지점으로 이동하고 지점으로 이동하지는 않습니다. + call , 그리고 매우 혼동되는 디버그 입니다.

올바른 수정 사항은 다음과 같습니다.

// 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();
}

즉, 반복자 블록 논리가있는 개인 헬퍼 메소드와 널 점검을 수행하고 반복자를 리턴하는 공용 표면 메소드를 작성하십시오. 이제 GetFrobs이 호출되면 null 검사가 즉시 수행 된 다음 시퀀스가 ​​반복 될 때 GetFrobsForReal이 실행됩니다.

LINQ to Objects의 참조 소스를 살펴보면이 기술이 전체적으로 사용되는 것을 볼 수 있습니다. 작성하는 것이 약간 더 복잡하지만 nullity 오류 디버깅이 훨씬 쉽습니다. 작성자의 편의가 아닌 발신자의 편의를 위해 코드를 최적화하십시오.

안전하지 않은 코드에서 null 역 참조에 대한 참고 사항

C #에는 "안전하지 않은"모드가 있는데, 이는 이름에서 알 수 있듯이 메모리 안전 및 형식 안전성을 제공하는 일반적인 안전 메커니즘이 적용되지 않기 때문에 매우 위험합니다. 메모리가 어떻게 작동하는지 철저하고 깊이 이해하지 않으면 안전하지 않은 코드를 작성해서는 안됩니다.

안전하지 않은 모드에서는 다음 두 가지 중요한 사실을 알고 있어야합니다.

  • null pointer를 참조 해제하면 null reference를 참조하지 않는 것과 동일한 예외가 발생합니다.
  • 널이 아닌 유효하지 않은 포인터를 참조 해제하면 can 일부 상황에서 해당 예외가 발생합니다.

그 이유를 이해하려면 .NET이 우선 null 역 참조 예외를 생성하는 방법을 이해하는 데 도움이됩니다. (이 세부 사항은 Windows에서 실행되는 .NET에 적용되며 다른 운영 체제에서도 유사한 메커니즘을 사용합니다.)

메모리는 Windows에서 가상화됩니다. 각 프로세스는 운영 체제에서 추적하는 많은 "페이지"메모리의 가상 메모리 공간을 확보합니다. 각 메모리 페이지에는 읽기, 쓰기, 실행 등 사용 방법을 결정하는 플래그가 설정되어 있습니다. lowest 페이지는 "어쨌든 사용 된 경우 오류 발생"으로 표시됩니다.

C #에서 Null 포인터와 Null 참조는 내부적으로 숫자 0으로 표시되므로 해당 메모리 저장소로이를 참조하려고하면 운영 체제에서 오류가 발생합니다. 그런 다음 .NET 런타임은이 오류를 감지하여 널 역 참조 예외로 바꿉니다.

따라서 널 포인터와 널 참조를 모두 역 참조하면 동일한 예외가 발생합니다.

두 번째 요점은 어떻습니까? 역 참조 any 가상 메모리의 가장 낮은 페이지에있는 잘못된 포인터는 동일한 운영 체제 오류를 유발하므로 동일한 예외가 발생합니다.

왜 이것이 의미가 있습니까? 두 개의 int를 포함하는 구조체와 null과 같은 관리되지 않는 포인터가 있다고 가정 해보십시오. 구조체에서 두 번째 int를 역 참조하려고하면 CLR은 위치 0에서 저장소에 액세스하려고 시도하지 않습니다. 위치 4의 스토리지에 액세스합니다. 그러나 논리적으로 이것은 null 역 참조입니다. 그 주소를 via null로 가져 오기 때문입니다.

안전하지 않은 코드로 작업하고 null 역 참조 예외가 발생하는 경우 문제가되는 포인터가 null 일 필요는 없습니다. 가장 낮은 페이지의 어느 위치 에나있을 수 있으며이 예외가 발생합니다.

2304
John Saunders

NullReference 예외 — Visual Basic

Visual Basic에 대한 _NullReference Exception_는 C #에있는 것과 다릅니다. 결국 둘 다 .NET Framework에 정의 된 것과 동일한 예외를보고합니다. Visual Basic에 고유 한 원인은 거의 없습니다 (아마도 하나만).

이 답변은 Visual Basic 용어, 구문 및 컨텍스트를 사용합니다. 사용 된 예제는 과거의 많은 Stack 오버플로 질문에서 비롯된 것입니다. 이는 게시물에서 자주 볼 수있는 상황의kinds를 사용하여 관련성을 최대화하기위한 것입니다. 그것을 필요로하는 사람들을위한 약간의 설명도 제공됩니다. 귀하와 유사한 예는very여기에 나와 있습니다.

참고 :

  1. 이것은 개념 기반입니다. 프로젝트에 붙여 넣을 코드가 없습니다. NullReferenceException (NRE)의 원인,이를 찾는 방법, 수정 방법 및이를 피하는 방법을 이해하는 데 도움이됩니다. NRE는 여러 가지 방법으로 발생할 수 있으므로 이것이 유일한 만남은 아닐 것입니다.
  2. 스택 (오버플로 게시물)의 예제가 항상 처음에 무언가를 수행하는 가장 좋은 방법을 보여주지는 않습니다.
  3. 일반적으로 가장 간단한 치료법이 사용됩니다.

기본 의미

"개체가 개체의 인스턴스로 설정되지 않았습니다"메시지는 초기화되지 않은 개체를 사용하려고 함을 나타냅니다. 이것은 다음 중 하나로 요약됩니다.

  • 코드declared객체 변수이지만initialize하지 않았습니다 (인스턴스를 만들거나 'instantiate')
  • 코드에서 객체를 초기화한다고 가정했지만
  • 다른 코드가 아직 사용중인 객체를 조기에 무효화했을 수 있습니다.

원인 찾기

문제는 Nothing 인 객체 참조이기 때문에 답을 찾아서 어느 것을 찾아야합니다. 그런 다음 초기화되지 않은 이유를 판별하십시오. 다양한 변수 위에 마우스를 대면 Visual Studio (VS)에 값이 표시됩니다. 원인은 Nothing입니다.

IDE debug display

또한 관련 코드, 특히 Catch 블록에 아무것도없는 Try/Catch 블록을 제거해야합니다. 이로 인해 Nothing 인 객체를 사용하려고하면 코드가 중단됩니다. 원하는 것입니다 문제의 정확한location을 식별하고 문제를 일으키는 개체를 식별 할 수 있기 때문입니다.

_Error while..._를 표시하는 Catch의 MsgBox은 거의 도움이되지 않습니다. 이 방법은very badStack Overflow 질문으로 이어집니다. 실제 예외, 관련된 객체 또는 발생하는 코드 라인을 설명 할 수 없기 때문입니다.

_Locals Window_ (Debug-> Windows-> Locals)를 사용하여 객체를 검사 할 수도 있습니다.

문제의 위치와 위치를 알고 나면 새 질문을 게시하는 것보다 수정하기가 쉽고 빠릅니다.

참조 :

예와 치료

클래스 객체/인스턴스 생성

_Dim reg As CashRegister
...
TextBox1.Text = reg.Amount         ' NRE
_

문제는 Dim이 CashRegister를 만들지 않는다는 것입니다.object; 해당 유형의 reg이라는 변수 만 선언합니다.Declaring객체 변수와instance생성은 서로 다른 두 가지입니다.

해결책

New 연산자를 사용하여 인스턴스를 선언 할 때 종종 인스턴스를 생성 할 수 있습니다.

_Dim reg As New CashRegister        ' [New] creates instance, invokes the constructor

' Longer, more explicit form:
Dim reg As CashRegister = New CashRegister
_

나중에 인스턴스를 작성하는 것이 적절한 경우 :

_Private reg As CashRegister         ' Declare
  ...
reg = New CashRegister()            ' Create instance
_

참고 : Do not ​​생성자 (_Sub New_)를 포함하여 프로 시저에서 Dim을 다시 사용하십시오.

_Private reg As CashRegister
'...

Public Sub New()
   '...
   Dim reg As New CashRegister
End Sub
_

그러면 해당 컨텍스트 (하위)에만있는local변수 reg이 생성됩니다. 다른 곳에서 사용할 모듈 수준 reg을 가진 Scope 변수는 Nothing으로 유지됩니다.

New 연산자가없는 것은 검토 된 스택 오버플로 질문에서 볼 수있는 _NullReference Exceptions의 # 1 원인입니다.

Visual Basic은 New을 사용하여 프로세스를 반복적으로 명확하게 만들려고합니다. New 연산자 사용 new 개체를 만들고 Sub New-생성자-객체가 다른 초기화를 수행 할 수있는 위치.

명확하게 말하면 Dim (또는 Private) 만 변수declares및 해당 Type입니다. 변수의Scope(전체 모듈/클래스에 존재하는지 또는 프로 시저에 로컬인지)은where에 의해 결정됩니다. _Private | Friend | Public_는Scope가 아닌 액세스 수준을 정의합니다.

자세한 내용은 다음을 참조하십시오.


배열

배열도 인스턴스화해야합니다.

_Private arr as String()
_

이 배열은 선언되지 않았으며 생성되지 않았습니다. 배열을 초기화하는 방법에는 여러 가지가 있습니다.

_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}
_

데이터 유형 및 배열 크기는 할당되는 데이터에서 유추됩니다. 클래스/모듈 레벨 선언에는 여전히 _As <Type>_와 함께 _Option Strict_이 필요합니다.

_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
_

자세한 내용은 다음을 참조하십시오.


리스트와 컬렉션

.NET 컬렉션 (목록, 사전 등 다양한 종류가 있음)도 인스턴스화하거나 만들어야합니다.

_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
_

barList은 (는) 인스턴스화되지 않고 선언 되었기 때문에 두 절차 중 하나에서 NRE가 발생합니다. Foo의 인스턴스를 만들면 내부 barList의 인스턴스도 만들어지지 않습니다. 생성자 에서이 작업을 수행하려는 의도 일 수 있습니다.

_Public Sub New         ' Constructor
    ' Stuff to do when a new Foo is created...
    barList = New List(Of Bar)
End Sub
_

이전과 같이 이것은 올바르지 않습니다.

_Public Sub New()
    ' Creates another barList local to this procedure
     Dim barList As New List(Of Bar)
End Sub
_

자세한 내용은 List(Of T) Class 를 참조하십시오.


데이터 제공자 객체

데이터베이스를 사용하면 한 번에 많은 개체 (Command, Connection, Transaction, Dataset, DataTable, DataRows....)가있을 수 있으므로 NullReference에 많은 기회가 제공됩니다. 참고 : 사용중인 데이터 공급자 (MySQL, SQL Server, OleDB 등)는 중요하지 않습니다.concepts는 같습니다.

예 1

_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 Dataset 객체가 선언되었지만 인스턴스가 생성되지 않았습니다. DataAdapter은 기존 DataSet을 채우지 않고 채 웁니다. 이 경우 ds은 로컬 변수이므로the IDE는 이러한 상황이 발생할 수 있음을 경고합니다:

img

con의 경우와 같이 모듈/클래스 수준 변수로 선언되면 컴파일러는 객체가 업스트림 프로 시저에 의해 생성되었는지 알 수 없습니다. 경고를 무시하지 마십시오.

해결책

_Dim ds As New DataSet
_

예제 2

_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...
_

예제

_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
_

DataAdapter은 이전 예에서와 같이 TableNames을 제공하지만 SQL 또는 데이터베이스 테이블에서 이름을 구문 분석하지는 않습니다. 결과적으로 ds.Tables("TICKET_RESERVATION")은 존재하지 않는 테이블을 참조합니다.

해결책은 동일합니다. 인덱스로 테이블을 참조하십시오.

_If ds.Tables(0).Rows.Count > 0 Then
_

DataTable Class 도 참조하십시오.


객체 경로/중첩

_If myFoo.Bar.Items IsNot Nothing Then
   ...
_

코드는 Items 만 테스트하는 반면 myFooBar은 모두 Nothing 일 수 있습니다. remedy는 한 번에 하나씩 전체 체인 또는 객체 경로를 테스트하는 것입니다.

_If (myFoo IsNot Nothing) AndAlso
    (myFoo.Bar IsNot Nothing) AndAlso
    (myFoo.Bar.Items IsNot Nothing) Then
    ....
_

AndAlso이 중요합니다. 첫 번째 False 조건이 발생하면 후속 테스트는 수행되지 않습니다. 이를 통해 코드는 한 번에 하나의 '레벨'로 객체를 안전하게 '드릴'할 수 있으며 myFoo이 유효한 것으로 판단 된 후에 만 ​​_myFoo.Bar_를 평가할 수 있습니다. 복잡한 객체를 코딩 할 때 객체 체인 또는 경로가 상당히 길어질 수 있습니다.

_myBase.myNodes(3).Layer.SubLayer.Foo.Files.Add("somefilename")
_

null 객체의 '다운 스트림'은 참조 할 수 없습니다. 이것은 컨트롤에도 적용됩니다.

_myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"
_

여기서 myWebBrowser 또는 Document은 Nothing이거나 _formfld1_ 요소가 없을 수 있습니다.


UI 컨트롤

_Dim cmd5 As New SqlCommand("select Cartons, Pieces, Foobar " _
     & "FROM Invoice where invoice_no = '" & _
     Me.ComboBox5.SelectedItem.ToString.Trim & "' And category = '" & _
     Me.ListBox1.SelectedItem.ToString.Trim & "' And item_name = '" & _
     Me.ComboBox2.SelectedValue.ToString.Trim & "' And expiry_date = '" & _
     Me.expiry.Text & "'", con)
_

무엇보다도이 코드는 사용자가 하나 이상의 UI 컨트롤에서 무언가를 선택하지 않았을 것으로 예상하지 않습니다. _ListBox1.SelectedItem_는 Nothing 일 수 있으므로 _ListBox1.SelectedItem.ToString_는 NRE가됩니다.

해결책

사용하기 전에 데이터의 유효성을 검사하십시오 (_Option Strict_ 및 SQL 매개 변수도 사용).

_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
_

또는 _(ComboBox5.SelectedItem IsNot Nothing) AndAlso..._를 사용할 수 있습니다


Visual Basic 양식

_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
_

이것은 NRE를 얻는 매우 일반적인 방법입니다. C #에서 코딩 방법에 따라 IDE는 Controls이 현재 컨텍스트에 존재하지 않거나 "정적이 아닌 멤버를 참조 할 수 없음"을보고합니다. 따라서 어느 정도 VB 전용 상황입니다. 또한 캐스케이드 장애가 발생할 수 있으므로 복잡합니다.

이 방법으로 배열과 컬렉션을 초기화 할 수 없습니다 .이 초기화 코드는before실행됩니다. 생성자가 Form 또는 Controls을 만듭니다. 결과적으로 :

  • 목록과 컬렉션은 단순히 비어 있습니다
  • 배열에는 Nothing의 다섯 가지 요소가 포함됩니다
  • _.Text_ 속성이없는 것이 없기 때문에 somevar 할당은 즉시 NRE가됩니다.

나중에 배열 요소를 참조하면 NRE가 발생합니다. 이상한 버그로 인해 _Form_Load_에서이 작업을 수행하면 IDEnot not예외가 발생할 때보고합니다. 코드에서 배열을 사용하려고하면 예외가later팝업으로 나타납니다. 이 "자동 예외"는 이 게시물에 자세히 설명되어 있음 입니다. 우리의 목적을 위해, 핵심은 양식 (_Sub New_ 또는 _Form Load_ 이벤트)을 작성하는 동안 치명적인 문제가 발생하면 예외가보고되지 않고 코드가 프로 시저를 종료하고 양식 만 표시한다는 것입니다.

NRE 이후에 _Sub New_ 또는 _Form Load_ 이벤트의 다른 코드가 실행되지 않으므로다른 많은 것들는 초기화되지 않은 상태로 남을 수 있습니다.

_Sub Form_Load(..._
   '...
   Dim name As String = NameBoxes(2).Text        ' NRE
   ' ...
   ' More code (which will likely not be executed)
   ' ...
End Sub
_

Note 이것은 다음의 경우 불법 인 모든 제어 및 구성 요소 참조에 적용됩니다 :

_Public Class Form1

    Private myFiles() As String = Me.OpenFileDialog1.FileName & ...
    Private dbcon As String = OpenFileDialog1.FileName & ";Jet Oledb..."
    Private studentName As String = TextBox13.Text
_

부분 치료법

VB가 경고를 제공하지 않는 것이 궁금하지만 해결책은 양식 레벨의 컨테이너 declare이지만 양식로드 이벤트 핸들러의 initialize입니다. 컨트롤 do가 존재할 때. 코드가 InitializeComponent 호출 이후에있는 한 _Sub New_에서 수행 할 수 있습니다.

_' 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
_

배열 코드가 아직 숲에서 나오지 않았을 수 있습니다. 컨테이너 컨트롤에있는 컨트롤 (예 : GroupBox 또는 Panel)은 _Me.Controls_에서 찾을 수 없습니다. 해당 패널 또는 GroupBox의 Controls 컬렉션에 있습니다. 제어 이름의 철자가 틀리면 제어도 리턴되지 않습니다 (_"TeStBox2"_). 이러한 경우 Nothing이 다시 해당 배열 요소에 저장되며이를 참조하려고하면 NRE가 발생합니다.

당신이 찾고있는 것을 알았으므로 이제 쉽게 찾을 수 있어야합니다. VS shows you the error of your ways

"Button2"는 Panel에 있습니다.

해결책

양식의 Controls 컬렉션을 사용하여 이름으로 간접 참조하는 대신 제어 참조를 사용하십시오.

_' 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
_

이것은 IDE가 '모든 경로가 값을 반환하는 것은 아니며 NullReferenceException이 결과'가 될 수 있음을 경고하는 경우입니다. _Exit Function_을 _Return Nothing_로 바꾸면 경고가 표시되지 않지만 문제가 해결되지는 않습니다. _someCondition = False_가 NRE가 될 때 리턴을 사용하려고 시도하는 모든 것 :

_bList = myFoo.BarList()
For Each b As Bar in bList      ' EXCEPTION
      ...
_

해결책

함수에서 _Exit Function_를 _Return bList_로 바꾸십시오.emptyList을 반환하는 것은 Nothing을 반환하는 것과 다릅니다. 리턴 된 오브젝트가 Nothing 일 가능성이있는 경우이를 사용하기 전에 테스트하십시오.

_ bList = myFoo.BarList()
 If bList IsNot Nothing Then...
_

잘못 구현 된 시도/캐치

잘못 구현 된 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
_

이것은 예상대로 객체가 생성되지 않는 경우이지만 빈 Catch의 카운터 유용성을 보여줍니다.

SQL에 여분의 쉼표가 있으며 ( 'mailaddress'뒤에) _.ExecuteReader_에서 예외가 발생합니다. Catch이 아무 작업도 수행하지 않으면 Finally이 정리를 수행하려고 시도하지만 Close을 (를) null DataReader 객체로 만들 수 없으므로 새로운 NullReferenceException 결과가 나타납니다.

Catch 블록은 악마의 놀이터입니다. 이 OP는 왜 Finally 블록에서 NRE를 받고 있었는지 당황했습니다. 다른 상황에서 빈 Catch을 사용하면 더 많은 다운 스트림이 건초로 연결되어 문제의 잘못된 장소에서 잘못된 것을 보는 데 시간을 소비 할 수 있습니다. 위에서 설명한 "자동 예외"는 동일한 엔터테인먼트 가치를 제공합니다.

해결책

빈 Try/Catch 블록을 사용하지 마십시오. 코드 충돌을 일으켜 a) 원인을 식별합니다. b) 위치를 식별하고 c) 적절한 해결책을 적용하십시오. Try/Catch 블록은 예외를 해결할 자격이있는 사람 (개발자)의 예외를 숨기려는 것이 아닙니다.


DBNull은 Nothing과 같지 않습니다

_For Each row As DataGridViewRow In dgvPlanning.Rows
    If Not IsDBNull(row.Cells(0).Value) Then
        ...
_

IsDBNull 함수는value가 _System.DBNull_와 같은지 테스트하는 데 사용됩니다. From MSDN :

System.DBNull 값은 Object가 없거나 존재하지 않는 데이터를 나타냅니다. DBNull은 Nothing과 같지 않으므로 변수가 아직 초기화되지 않았 음을 나타냅니다.

해결책

_If row.Cells(0) IsNot Nothing Then ...
_

이전과 같이 Nothing을 테스트 한 다음 특정 값을 테스트 할 수 있습니다.

_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
       ...
_

FirstOrDefault은 첫 번째 항목 또는 기본값을 반환합니다.이 값은 참조 유형의 경우 Nothing이며 DBNull은 아닙니다.

_If getFoo IsNot Nothing Then...
_

통제 수단

_Dim chk As CheckBox

chk = CType(Me.Controls(chkName), CheckBox)
If chk.Checked Then
    Return chk
End If
_

CheckBox이있는 chkName을 찾을 수 없거나 GroupBox에있는 경우 chk은 아무것도 없으며 속성을 참조하려고하면 예외가 발생합니다.

해결책

_If (chk IsNot Nothing) AndAlso (chk.Checked) Then ...
_

DataGridView

DGV에는 다음과 같은 몇 가지 단점이 있습니다.

_dgvBooks.DataSource = loan.Books
dgvBooks.Columns("ISBN").Visible = True       ' NullReferenceException
dgvBooks.Columns("Title").DefaultCellStyle.Format = "C"
dgvBooks.Columns("Author").DefaultCellStyle.Format = "C"
dgvBooks.Columns("Price").DefaultCellStyle.Format = "C"
_

dgvBooks에 _AutoGenerateColumns = True_이 있으면 열이 만들어 지지만 이름을 지정하지 않으므로 이름으로 참조 할 때 위의 코드가 실패합니다.

해결책

열 이름을 수동으로 지정하거나 색인으로 참조하십시오.

_dgvBooks.Columns(0).Visible = True
_

예 2 — NewRow에주의

_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
_

DataGridViewAllowUserToAddRowsTrue (기본값) 인 경우 맨 아래에있는 빈/새 행의 Cells에는 모두 Nothing이 포함됩니다. 내용을 사용하려는 대부분의 시도 (예 : ToString)는 NRE가됩니다.

해결책

_For/Each_ 루프를 사용하고 IsNewRow 속성을 테스트하여 마지막 행인지 확인하십시오. 이것은 AllowUserToAddRows이 true인지 아닌지 작동합니다.

_For Each r As DataGridViewRow in myDGV.Rows
    If r.IsNewRow = False Then
         ' ok to use this row
_

_For n_ 루프를 사용하는 경우 IsNewRow이 true 일 때 행 수를 수정하거나 _Exit For_을 사용하십시오.


내 설정 (StringCollection)

특정 상황에서 StringCollection 인 _My.Settings_의 항목을 사용하려고하면 처음 사용할 때 NullReference가 발생할 수 있습니다. 해결책은 동일하지만 명확하지 않습니다. 중히 여기다:

_My.Settings.FooBars.Add("ziggy")         ' foobars is a string collection
_

VB가 설정을 관리하고 있으므로 컬렉션을 초기화하는 것이 합리적입니다. 이전에 컬렉션에 초기 항목을 추가 한 경우에만 설정 편집기에서 수행됩니다. 항목이 추가 될 때 컬렉션이 (명확하게) 초기화되기 때문에 설정 편집기에 추가 할 항목이 없으면 Nothing으로 유지됩니다.

해결책

필요한 경우 양식의 Load 이벤트 핸들러에서 설정 콜렉션을 초기화하십시오.

_If My.Settings.FooBars Is Nothing Then
    My.Settings.FooBars = New System.Collections.Specialized.StringCollection
End If
_

일반적으로 Settings 컬렉션은 응용 프로그램을 처음 실행할 때만 초기화하면됩니다. 다른 해결 방법은 프로젝트-> 설정 | FooBars, 프로젝트를 저장 한 다음 가짜 값을 제거하십시오.


키 포인트

New 연산자를 잊었을 수 있습니다.

또는

초기화 된 객체를 코드에 반환하기 위해 완벽하게 수행한다고 가정하지는 않았습니다.

컴파일러 경고를 무시하지 말고 _Option Strict On_ (항상)을 사용하십시오.


MSDN NullReference 예외

302

또 다른 시나리오는 널 오브젝트를 값 유형 으로 변환 할 때입니다. 예를 들어, 아래 코드는 :

object o = null;
DateTime d = (DateTime)o;

그것은 캐스팅에 NullReferenceException을 던질 것입니다. 위의 샘플에서는 매우 분명해 보이지만, 소유하지 않은 코드에서 null 개체가 반환 된 "후기 바인딩"복잡한 시나리오에서 발생할 수 있습니다. 예를 들어 일부 자동 시스템에서 캐스트가 생성되었습니다.

한 가지 예가 Calendar 컨트롤이있는이 간단한 ASP.NET 바인딩 조각입니다.

<asp:Calendar runat="server" SelectedDate="<%#Bind("Something")%>" />

SelectedDate은 실제로 DateTime Web Control 유형의 Calendar 유형의 속성이며 바인딩은 null을 완벽하게 반환 할 수 있습니다. 암시 적 ASP.NET 생성기는 위의 캐스트 코드와 동일한 코드 조각을 만듭니다. 그리고 이것은 컴파일하기에 ASP.NET에서 생성 된 코드에 있기 때문에 NullReferenceException을 발생시키는 것이 매우 어렵습니다.

223
Simon Mourier

그것은 문제의 변수가 아무 것도 가리키고 있지 않다는 것을 의미합니다. 나는 이것을 이렇게 생성 할 수있다 :

SqlConnection connection = null;
connection.Open();

변수 "connection"을 선언 한 동안 아무 것도 가리 키지 않았기 때문에 오류가 발생합니다. "Open"멤버를 호출하려고하면 해결할 참조가 없으므로 오류가 발생합니다.

이 오류를 방지하려면 다음과 같이하십시오.

  1. 객체로 무엇인가를 시도하기 전에 항상 객체를 초기화하십시오.
  2. 객체가 null인지 확실하지 않으면 object == null로 확인하십시오.

JetBrains의 Resharper 도구는 널 참조 오류가 발생할 수있는 코드의 모든 위치를 식별하여 널 확인을 허용합니다. 이 오류는 IMHO의 가장 큰 버그 원인입니다.

154
Chris B. Behrens

즉, 코드에서 null로 설정된 객체 참조 변수가 사용되었습니다 (즉, 실제 객체 인스턴스를 참조하지 않음).

오류를 방지하려면 널이 될 수있는 오브젝트를 사용하기 전에 널에 대해 테스트해야합니다.

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
}
150
Jonathan Wood

시나리오에 관계없이 원인은 .NET에서 항상 동일합니다.

값이 Nothing/null 인 참조 변수를 사용하려고합니다. 값이 참조 변수에 대해 Nothing/null이면 힙에있는 모든 객체의 인스턴스에 대한 참조를 실제로 보유하고 있지 않음을 의미합니다.

변수에 할당 된 값의 인스턴스를 작성하지 않았거나 변수를 수동으로 Nothing/null과 같게 설정하거나 변수를 Nothing/null으로 설정하는 함수를 호출했습니다.

94
code master

던져지는이 예외의 예는 다음과 같습니다. 무언가를 검사하려고하면 null입니다.

예 :

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
} 

NET 런타임은 인스턴스화되지 않은 무언가, 즉 위의 코드에서 작업을 수행하려고하면 NullReferenceException을 발생시킵니다.

ArgumentNullException과 비교할 때, 메소드가 전달되는 것이 null이 아니라는 것을 예상 할 경우 방어적인 수단으로 던져집니다.

자세한 내용은C # NullReferenceException 및 Null 매개 변수.

85
Alex KeySmith

참조 유형을 초기화하지 않은 상태에서 속성 중 하나를 설정하거나 읽으려면 NullReferenceException 이 발생합니다.

예:

Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.

변수가 null이 아닌지 확인하여 간단히이를 피할 수 있습니다.

Person p = null;
if (p!=null)
{
    p.Name = "Harry"; // Not going to run to this point
}

NullReferenceException이 발생하는 이유를 완전히 이해하려면 값 유형참조 유형 의 차이점을 알아야합니다.

따라서 값 유형 을 처리하는 경우 NullReferenceExceptions은 not 발생할 수 있습니다. 참조 유형 !을 처리 할 때주의해야합니다.

참조 형식 만이 이름에서 알 수 있듯이 참조를 유지하거나 문자 그대로 아무 것도 가리킬 수 없습니다 (또는 'null'). 값 유형에는 항상 값이 들어 있습니다.

참조 유형 (이 중 하나를 선택해야 함) :

  • 동적
  • 목적

값 유형 (이 값은 무시할 수 있음) :

  • 숫자 유형
  • 통합 유형
  • 부동 소수점 유형
  • 소수
  • 불량배
  • 사용자 정의 구조체
81
Fabian Bigler

NullReferenceExceptions이 발생할 수있는 또 다른 경우는 as 연산자의 잘못된 사용입니다 :

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

여기서 BookCar은 호환되지 않는 유형입니다. CarBook으로 변환/변환 될 수 없습니다. 이 캐스트가 실패하면 asnull을 반환합니다. 이 후에 mybook을 사용하면 NullReferenceException이 발생합니다.

일반적으로 다음과 같이 캐스트 또는 as을 사용해야합니다.

유형 변환이 항상 성공할 것으로 예상하는 경우 (즉, 객체가 미리 있어야 할 것임을 알고 있다면) 캐스트를 사용해야합니다.

ComicBook cb = (ComicBook)specificBook;

유형이 확실하지 않지만 특정 유형으로 사용하려면 try 를 사용하려면 as을 사용하십시오.

ComicBook cb = specificBook as ComicBook;
if (cb != null) {
   // ...
}
75

whatNullReferenceExceptions 를 유발하고 avoid/fix에 대한 접근 방식이 다른 답변에서 해결되었지만 많은 프로그래머들이 아직 배우지 못한 것들 개발하는 동안 이러한 예외를 독립적으로 debug하는 방법입니다.

Visual Studio에서는 일반적으로 Visual Studio Debugger 덕분에 쉽습니다.


먼저 올바른 오류가 발생하는지 확인하십시오. VS2010의 'System.NullReferenceException'을 어떻게 차단합니까?참고1

그런 다음 디버깅으로 시작 (F5) 또는 [VS 디버거]를 실행중인 프로세스에 연결 . 경우에 따라 Debugger.Break 를 사용하면 디버거를 시작하라는 메시지가 표시됩니다.

이제 NullReferenceException이 발생하거나 처리되지 않으면 디버거는 예외가 발생한 줄에서 중지됩니다 (위의 규칙을 기억하십니까?). 때로는 오류를 쉽게 발견 할 수 있습니다.

예를 들어, 다음 줄에서 can 예외를 일으키는 유일한 코드는 myString이 null로 평가되는 경우입니다. Watch Window 를 보거나 Immediate Window 에서 식을 실행하여 확인할 수 있습니다.

_var x = myString.Trim();
_

다음과 같은보다 고급 인 경우 위의 기술 중 하나 (Watch 또는 Immediate Windows)를 사용하여 식을 검사하여 _str1_이 null인지 또는 _str2_가 null인지 확인해야합니다. .

_var x = str1.Trim() + str2.Trim();
_

일단 where 예외가 발생하면, 널값이 [부적절하게] 도입 된 곳을 찾기 위해 역으로 추론하는 것은 보통 사소한 일입니다.

예외의 원인을 이해하는 데 필요한 시간을 가지십시오. 널 표현식을 검사하십시오. 이러한 널 표현식을 초래할 수있는 이전 표현식을 검사하십시오. breakpoints 를 추가하고 프로그램을 적절히 단계별로 진행하십시오. 디버거를 사용하십시오.


1 Break on Throws가 너무 공격적이고 .NET 또는 타사 라이브러리의 NPE에서 디버거가 중지되면 Break on User-Unhandled 를 사용하여 포착 된 예외를 제한 할 수 있습니다. 또한 VS2012에는 Just My Code 가 도입되어 활성화하는 것이 좋습니다.

Just My Code를 사용하여 디버깅하는 경우 동작이 약간 다릅니다. 내 코드 만 사용으로 설정하면 디버거는 내 코드 외부에서 발생하고 내 코드를 통과하지 못하는 CLR (first-chance common language runtime) 예외를 무시합니다.

63
user2864740

널값 참조를 포함하는 오브젝트를 사용 중입니다. 그래서 그것은 null 예외를 제공합니다. 이 예에서 문자열 값은 null이고 길이를 확인할 때 예외가 발생했습니다.

예:

string value = null;
if (value.Length == 0) // <-- Causes exception
{
    Console.WriteLine(value); // <-- Never reached
}

예외 오류는 다음과 같습니다.

처리되지 않은 예외 :

System.NullReferenceException : 개체 참조가 개체의 인스턴스로 설정되지 않았습니다. Program.Main에서 ()

63
user1814380

Simon Mourier가이 예를 들었습니다. :

object o = null;
DateTime d = (DateTime)o;  // NullReferenceException

여기서 unboxing 변환 (캐스트) fromobject 또는 클래스 System.ValueType 또는 System.Enum 중 하나 또는 인터페이스 유형) 값 유형 (Nullable<> ) 자체가 NullReferenceException을 제공합니다.

다른 방향에서는 boxing conversion a Nullable<>에서 HasValuefalseto참조 유형), null 참조를 제공 할 수 있으며 이후에 NullReferenceException. 고전적인 예는 다음과 같습니다.

DateTime? d = null;
var s = d.ToString();  // OK, no exception (no boxing), returns ""
var t = d.GetType();   // Bang! d is boxed, NullReferenceException

때때로 권투는 다른 방법으로 발생합니다. 예를 들어 다음과 같은 비 일반 확장 방법을 예로들 수 있습니다.

public static void MyExtension(this object x)
{
  x.ToString();
}

다음 코드는 문제가 될 수 있습니다.

DateTime? d = null;
d.MyExtension();  // Leads to boxing, NullReferenceException occurs inside the body of the called method, not here.

이러한 경우는 boxing Nullable<> 인스턴스가 실행될 때 런타임에서 사용하는 특수 규칙으로 인해 발생합니다.

55
Jeppe Stig Nielsen

엔티티 프레임 워크에서 사용되는 엔티티의 클래스 이름이 웹 양식 분리 기호 파일의 클래스 이름과 동일한 경우를 추가합니다.

Codebehind 클래스가 Contact이고 연락처 이름이 Contact.aspx 인 웹 양식이 있다고 가정합니다.

다음 코드는 context.SaveChanges ()를 호출 할 때 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;}
}

엔티티와 코드 숨김 클래스가 모두 동일한 네임 스페이스에있는 경우 오류가 발생합니다. 이 문제를 해결하려면 Contact.aspx의 엔터티 클래스 나 코드 숨김 클래스의 이름을 바꿉니다.

이유 나는 이유에 대해 아직 확실하지 않다. 그러나 엔티티 클래스 중 하나가 System.Web.UI.Page를 확장 할 때마다이 오류가 발생합니다.

토론을 위해 DbContext.saveChanges ()의 NullReferenceException을 살펴보십시오.

40
AbhinavRanjan

나는 이것에 대답하는 데 다른 관점을 가지고 있습니다. 이런 종류의 답변 "이를 피하기 위해 무엇을 할 수 있습니까?"

예를 들어 MVC 응용 프로그램에서 다른 계층 간 작업을 수행하는 경우 컨트롤러는 비즈니스 작업을 호출하는 서비스가 필요합니다. 이러한 시나리오에서 종속 주입 컨테이너를 사용하여 서비스를 초기화하여 NullReferenceException을 피할 수 있습니다. 따라서 null 검사에 대해 걱정할 필요가 없으며 컨트롤러에서 서비스를 항상 단일 또는 프로토 타입으로 사용할 수있는 것처럼 호출 할 필요가 있습니다.

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();
    }
}
39
Mukus

이 예외를받을 수있는 또 다른 일반적인 경우에는 단위 테스트 중에 클래스 조롱이 포함됩니다. 조롱 프레임 워크가 사용되는 것과 관계없이 클래스 계층의 모든 적절한 수준이 올바르게 조롱되는지 확인해야합니다. 특히 테스트중인 코드에서 참조하는 HttpContext의 모든 속성을 조롱해야합니다.

좀 더 자세한 예제를 보려면 " 사용자 정의 AuthorizationAttribute를 테스트 할 때 throw되는 NullReferenceException "을 참조하십시오.

39
John Saunders

"어떻게해야합니까" 문제에 대해서는 많은 답변이있을 수 있습니다.

이러한 오류 조건을 방지하는보다 "공식적인"방법 개발 중 적용 계약에 의해 설계 귀하의 코드. 이것은 클래스 invariants 및/또는 function/method preconditions postconditions = 개발하는 동안 시스템에서.

즉, class invariants 클래스에서 정상적인 사용시 위반되지 않는 제약 조건이 있는지 확인하십시오. 따라서 클래스는 not 일관성이없는 상태가됩니다). Preconditions 함수/메소드에 입력으로 제공된 데이터는 일부 제약 조건을 따라야하며 never 위반하고 postconditions 함수/메소드 출력이 설정 제한 조건을 다시 위반하지 않고 다시 설정 제한 조건을 따라야 함을 의미합니다. 버그가없는 프로그램을 실행하는 동안 계약 조건을 never 위반해야합니다. 따라서 계약에 의한 설계는 디버그 모드에서 실제로 확인되며 릴리스에서 비활성화 됨 , 개발 된 시스템 성능을 최대화합니다.

이런 식으로 제약 조건 설정을 위반 한 NullReferenceException 사례를 피할 수 있습니다. 예를 들어 클래스에서 객체 속성 X을 사용하고 나중에 해당 메소드 중 하나를 호출하려고 시도하고 X에 null 값이 있으면 NullReferenceException이됩니다.

public X { get; set; }

public void InvokeX()
{
    X.DoSomething(); // if X value is null, you will get a NullReferenceException
}

그러나 "속성 X에 null 값이 없어야 함"을 메소드 전제 조건으로 설정하면 앞에서 설명한 시나리오를 막을 수 있습니다.

//Using code contracts:
[ContractInvariantMethod]
protected void ObjectInvariant () 
{
    Contract.Invariant ( X != null );
    //...
}

이러한 이유로 Code Contracts .NET 응용 프로그램 용 프로젝트가 있습니다.

또는 어설 션를 사용하여 계약 별 디자인을 적용 할 수 있습니다.

PDATE :이 용어는 Bertrand Meyer가 만든 것으로 언급 할 가치가 있습니다. Eiffel 프로그래밍 언어 디자인과 관련하여 .

37
Nick Louloudakis

NullReferenceException은 null 객체의 속성에 액세스하려고하거나 문자열 값이 비어서 문자열 메소드에 액세스하려고 할 때 발생합니다.

예 :

  1. 빈 문자열의 문자열 메소드가 액세스 될 때 :

    string str = string.Empty;
    str.ToLower(); // throw null reference exception
    
  2. Null 객체의 속성에 액세스 할 때 :

    Public Class Person {
        public string Name { get; set; }
    }
    Person objPerson;
    objPerson.Name  /// throw Null refernce Exception 
    
34
Hemant Bavle

TL; DR :Renderpage 대신 Html.Partial를 사용해보십시오.


다음과 같이 모델을 전송하여 뷰 내에서 뷰를 렌더링하려고 할 때 Object reference not set to an instance of an object를 얻었습니다.

@{
    MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null

디버깅 결과 모델이 MyOtherView 내부에 Null 인 것으로 나타났습니다. 내가 그것을 바꿀 때까지 :

@{
    MyEntity M = new MyEntity();
}
@Html.Partial("_MyOtherView.cshtml", M);

그리고 효과가있었습니다.

또한, Html.Partial을 (를) 시작하지 않은 이유는 Visual Studio 때때로 다르게 구성된 foreach 루프 내부에있는 경우 Html.Partial 아래에 오류가있는 구불 구불 한 줄을 던지기 때문입니다. 실제로 오류는 아니지만 :

@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>

그러나이 "오류"에 문제없이 응용 프로그램을 실행할 수있었습니다. foreach 루프의 구조를 다음과 같이 변경하여 오류를 제거 할 수있었습니다.

@foreach(var M in MyEntities){
    ...
}

Visual Studio가 앰퍼샌드와 괄호를 잘못 읽고 있기 때문에 느낌이 들었습니다.

30
Travis Heeter

당신은 그것에 대해 무엇을 할 수 있습니까?

여기에는 null 참조가 무엇인지 설명하고 디버깅하는 방법에 대한 좋은 답변이 많이 있습니다. 그러나 문제를 예방하거나 최소한 잡기 쉽도록하는 방법은 거의 없습니다.

인수 검사

예를 들어 메소드는 서로 다른 인수를 검사하여 null인지 확인하고 ArgumentNullException을 throw 할 수 있습니다.이 예외는이 정확한 목적을 위해 작성된 예외입니다.

ArgumentNullException의 생성자는 매개 변수의 이름과 메시지를 인수로 사용하여 개발자가 정확하게 문제를 알릴 수 있습니다.

public void DoSomething(MyObject obj) {
    if(obj == null) 
    {
        throw new ArgumentNullException("obj", "Need a reference to obj.");
    }
}

도구 사용

또한 도움이되는 여러 도서관이 있습니다. 예를 들어 "Resharper"는 코드를 작성하는 동안 특히 그 속성을 사용하는 경우 경고를 제공 할 수 있습니다. NotNullAttribute

런타임 및 컴파일 검사를 제공하는 Contract.Requires(obj != null)과 같은 구문을 사용하는 "Microsoft 코드 계약"이 있습니다. 코드 계약 소개 .

다음과 같은 속성을 사용할 수있게 해주는 "PostSharp"도 있습니다.

public void DoSometing([NotNull] obj)

그렇게하면 빌드 프로세스의 PostSharp 부분을 obj로 만들면 런타임에 null이 검사됩니다. 참조 : PostSharp null check

일반 코드 솔루션

또는 평범한 구식 코드를 사용하여 언제든지 직접 접근 방식을 코딩 할 수 있습니다. 예를 들어 null 참조를 포착하는 데 사용할 수있는 구조체가 있습니다. 그것은 Nullable<T>와 같은 개념을 모델로합니다 :

[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 };
    }
}

null을 허용하지 않으려면 Nullable<T>를 사용하는 것과 매우 비슷한 방법을 사용합니다. 여기 예시들이 있습니다 :

NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null

NotNull<T>는 암시 적으로 T과 짝수 캐스트되므로 어디서든 필요할 때 사용할 수 있습니다. 예를 들어, NotNull<Person>를 취하는 메소드에 Person 객체를 전달할 수 있습니다.

Person person = new Person { Name = "John" };
WriteName(person);

public static void WriteName(NotNull<Person> person)
{
    Console.WriteLine(person.Value.Name);
}

위의 nullable과 마찬가지로 Value 속성을 통해 기본 값에 액세스 할 수 있습니다. 또는 명시 적 또는 암시 적 캐스트를 사용할 수 있으며 아래 반환 값이있는 예제를 볼 수 있습니다.

Person person = GetPerson();

public static NotNull<Person> GetPerson()
{
    return new Person { Name = "John" };
}

또는 메서드가 캐스트를 수행하여 T (이 경우 Person)을 반환 할 때도 사용할 수 있습니다. 예를 들어, 다음 코드는 위의 코드와 같습니다.

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;
    }
}

다음은 어떻게 사용할 수 있는지에 대한 예입니다.

var person = GetPerson().NotNull();

GitHub

참고로 GitHub에서 위의 코드를 사용할 수 있습니다. 다음 위치에서 찾을 수 있습니다.

https://github.com/luisperezphd/NotNull

관련 언어 기능

C # 6.0에서는이 경우에 도움이되는 "null 조건부 연산자"가 도입되었습니다. 이 기능을 사용하여 중첩 된 객체를 참조 할 수 있으며 그 중 하나가 null이면 전체 표현식이 null을 반환합니다.

이렇게하면 경우에 따라 수행해야하는 검사의 수를 줄일 수 있습니다. 구문은 각 점 앞에 물음표를 넣는 것입니다. 예를 들어, 다음 코드를 취하십시오.

var address = country?.State?.County?.City;

countryCountry이라는 속성을 가진 State 유형의 객체라고 가정 해보십시오. country, State, County 또는 Citynull이면 address will benull. Therefore you only have to check whetheraddressisnull`입니다.

훌륭한 기능이지만 정보가 적습니다. 4 중 어느 것이 널인지는 분명하지 않습니다.

Nullable과 같은 내장?

C #에는 Nullable<T>에 대한 멋진 속기가 있습니다. 그래서 int?와 같은 형식 뒤에 물음표를 넣어 무효화 할 수 있습니다.

C #에 위와 같은 NotNull<T> 구조체가 있고 비슷한 축약 형, 느낌표 (!)를 사용하면 다음과 같이 작성할 수 있습니다. public void WriteName(Person! person).

22
Luis Perez

C # 6의 Null 조건부 연산자를 사용하여 NullReferenceException을 깨끗한 방법으로 수정할 수 있으며 Null 검사를 처리 할 코드를 적게 작성할 수 있습니다.

멤버 액세스 (?.) 또는 인덱스 (?) 연산을 수행하기 전에 null을 테스트하는 데 사용됩니다.

  var name = p?.Spouse?.FirstName;

다음과 같습니다.

    if (p != null)
    {
        if (p.Spouse != null)
        {
            name = p.Spouse.FirstName;
        }
    }

결과는 p가 null이거나 p.Spouse가 null 일 때 이름이 널이됩니다.

그렇지 않으면 변수 이름에 p.Spouse.FirstName의 값이 할당됩니다.

추가 정보 : 널 조건부 연산자

9
M.Hassan

오류 줄 "개체 참조가 개체의 인스턴스로 설정되지 않았습니다."는 개체 참조를 개체 개체에 할당하지 않았고 해당 개체의 특성/메서드에 여전히 액세스하고 있음을 나타냅니다.

예를 들어 myClass라는 클래스가 있고 prop1이라는 하나의 속성이 있다고 가정 해 보겠습니다.

public Class myClass
{
   public int prop1 {get;set;}
}

이제 다음과 같은 다른 클래스에서이 prop1에 액세스하고 있습니다.

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref.prop1 = 1;  //This line throws error
     }
}

myClass 클래스의 참조가 선언되었지만 인스턴스화되지 않았거나 객체의 인스턴스가 해당 클래스의 참조에 지정되지 않았기 때문에 위의 행에서 오류가 발생합니다.

이 문제를 해결하려면 인스턴스화해야합니다 (해당 클래스의 참조에 객체 할당).

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref = new myClass();
        ref.prop1 = 1;  
     }
}
8
Jaimin Dave

흥미롭게도,이 페이지의 답변 중 두 가지 엣지 사례에 대해 언급 한 답변이 없으며, 내가 추가해도 아무도 마음이 들지 않기를 바랍니다.

Edge case # 1 : 사전에 동시 액세스

.NET의 일반 사전은 스레드로부터 안전하지 않으며 두 개의 동시 스레드에서 키에 액세스하려고하면 가끔NullReference 또는 KeyNotFoundException을 throw 할 수 있습니다. 이 경우에는 예외가 상당히 오도합니다.

가장자리 케이스 # 2 : 안전하지 않은 코드

NullReferenceExceptionunsafe 코드에 의해 throw되면 포인터 변수를보고 IntPtr.Zero 또는 무엇인가를 확인하십시오. 어떤 것은 똑같은 ( "널 포인터 예외") 것이지만, 안전하지 않은 코드에서는 변수가 종종 값 유형/배열 등으로 형변환되며, 벽에 머리를 댄 채로 값 유형이이를 던질 수 있는지 궁금해합니다 예외.

(당신이 그것을 필요로하지 않는 한 안전하지 않은 코드를 사용하지 않는 또 다른 이유)

8
jazzcat

NullReferenceException 또는 개체 참조가 개체의 인스턴스로 설정되지 않은 경우 사용하려는 클래스의 개체가 인스턴스화되지 않은 경우 발생합니다. 예 :

Student라는 클래스가 있다고 가정합니다.

public class Student
{
    private string FirstName;
    private string LastName;
    public string GetFullName()
    {
        return FirstName + LastName;
    }
}

이제 학생의 성을 검색하려는 다른 클래스를 생각해보십시오.

public class StudentInfo
{      
    public string GetStudentName()
    {
        Student s;
        string fullname = s.GetFullName();
        return fullname;
    }        
}

위의 코드에서 볼 수 있듯이 Student s - 문은 Student 유형의 변수 만 선언하지만 Student 클래스는이 시점에서 인스턴스화되지 않습니다. 따라서, s.GetFullName () 문이 실행되면 NullReferenceException이 발생합니다.

3
Nick

글쎄, 간단한 용어로 :

작성되지 않았거나 현재 메모리에없는 오브젝트에 액세스하려고합니다.

그래서이 문제를 해결하는 방법 :

  1. 디버깅을 중단하고 디버거가 멈추도록합니다. 바로 깨진 변수로 이동합니다 ... 이제는이 문제를 해결하는 것입니다. 해당 위치에서 new 키워드를 사용하십시오.

  2. 객체가 존재하지 않기 때문에 일부 database commands에서 발생하면 null check를 수행하고 처리해야합니다.

    if (i == null) {
        // Handle this
    }
    
  3. 가장 어려운 것 ..GC오브젝트를 이미 수집 한 경우 ... 이것은 일반적으로 문자열을 사용하여 오브젝트를 찾으려고 할 때 발생합니다 ... 즉, 이름으로 찾습니다 객체의 GC가 이미 정리되었을 수도 있습니다 ... 이것은 찾기가 어렵고 상당히 문제가 될 것입니다 ...이 문제를 해결하는 더 좋은 방법은 개발 프로세스 중에 필요한 경우 null 체크를하는 것입니다. 이렇게하면 많은 시간을 절약 할 수 있습니다.

이름으로 찾기 란 문자열을 사용하여 FIndObject를 사용할 수있는 프레임 워크를 의미하며 코드는 다음과 같이 보일 수 있습니다. FindObject ( "ObjectName");

2
Akash Gutha

말 그대로 NullReferenceExeption을 수정하는 가장 쉬운 방법은 두 가지 방법이 있습니다. 예를 들어 스크립트가 첨부 된 GameObject가 있고 변수가 rb (rigidbody) 인 경우이 변수는 게임을 시작할 때 null을 시작합니다.
컴퓨터에 해당 변수에 저장된 데이터가 없으므로 NullReferenceExeption이 표시됩니다.

예를 들어 RigidBody 변수를 사용할 것입니다.
우리는 실제로 몇 가지 방법으로 데이터를 추가 할 수 있습니다.

  1. AddComponent> Physics> Rigidbody를 사용하여 오브젝트에 RigidBody 추가
    그런 다음 스크립트로 가서 rb = GetComponent<Rigidbody>();을 입력하십시오.
    이 코드 줄은 Start() 또는 Awake() 함수에서 가장 잘 작동합니다.
  2. 프로그래밍 방식으로 구성 요소를 추가하고 동시에 한 줄의 코드로 변수를 할당 할 수 있습니다. rb = AddComponent<RigidBody>();

추가 정보 : 개체에 개체를 추가하고 개체를 추가하는 것을 잊어 버린 경우 클래스 선언 (모든 사용자 용도 아래 공간) 위에 [RequireComponent(typeof(RigidBody))]을 입력 할 수 있습니다.
게임을 즐겁게 즐기십시오!

0
CausticLasagne

이 예외가 던져 질 수있는 일반적인 시나리오를 고려하면 맨 위에 객체가있는 속성에 액세스합니다.

전의:

string postalcode=Customer.Address.PostalCode; 
//if customer or address is null , this will through exeption

여기에서 address가 null이면 NullReferenceException이 발생합니다.

연습으로 우리는 항상 그러한 객체의 속성에 액세스하기 전에 null 체크를 사용해야합니다 (특히 일반적인 경우)

string postalcode=Customer?.Address?.PostalCode;
//if customer or address is null , this will return null, without through a exception
0
Hiran