NullReferenceException은 무엇이며 어떻게 해결합니까?


1875

나는 약간의 코드를 가지고 있으며 실행될 때,을 던진다 NullReferenceException.

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

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


- VS 2017에서 예외 도우미는이 예외의 원인 진단에 많은 도움이 될 것입니다 blogs.msdn.microsoft.com/visualstudio/2016/11/28/... 에서 새로운 예외 도우미 .
Zev Spitz

미래의 방문객 여러분,이 질문에 대한 답변은 ArgumentNullException에 동일하게 적용됩니다 . 귀하의 질문이이 질문의 복제본으로 닫히고 ANE가 발생하는 경우, 답변의 지시 사항에 따라 문제점을 디버그하고 수정하십시오.

@will ANE는 null이 매개 변수로 전달 된 경우에만 발생합니다. ANE 질문이이 질문과 중복되는 경우 예를 들어 줄 수 있습니까?
존 손더스

메타에 등장했지만 링크를 파헤쳐 야합니다. 그러나 그 의견에 따르면 ANE는 단순히 NRE이지만 누군가가 선점 검사를 추가했으며 적어도 null이 무엇인지 정확히 알고 있습니다 (인수 이름이 제공됨). 직접적인 NRE보다 진단하기가 조금 더 쉽습니다.

답변:


2415

원인이 무엇입니까?

결론

당신은 뭔가 사용하려는 null(또는 NothingVB.NET에서 참조). 즉,로 설정 null했거나 전혀 설정하지 않았습니다.

다른 것과 마찬가지로, null전달됩니다. 이 경우 null 방법 "A", 그 방법 "B"가 전달 될 수 null 방법 "A".

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

    1. 하는 개체 변수 초기화되지 않은 따라서 아무것도 가리 킵니다. 이 경우 해당 객체의 속성 또는 메서드에 액세스하면 이로 인해 NullReferenceException.
    1. 개발자는 의미있는 가치가 없음을 나타 내기 위해 의도적으로 사용하고 null있습니다. C #에는 변수에 대한 널 입력 가능 데이터 유형 개념이 있습니다 (데이터베이스 테이블에 널 입력 가능 필드가있을 수 있음). null예를 들어 int? a = null;물음표에 널 저장 가능 변수 a. 당신은 함께 그 중 하나를 확인할 수 있습니다 if (a.HasValue) {...}또는 함께 if (a==null) {...}. a이 예제 와 같이 널 입력 가능 변수 를 통해 a.Value명시 적으로 또는 일반 via를 통해 값에 액세스 할 수 있습니다 a.
      참고 통해 접속하는 것을 a.Value발생 InvalidOperationException대신에 (A)의 NullReferenceException경우 aIS를null-사전에 확인을 수행해야합니다. 즉, 다른 널 입력 가능 변수 int b;가있는 if (a.HasValue) { b = a.Value; }경우 더 작거나 같은 지정을 수행해야합니다 if (a != null) { b = a; }.

이 기사의 나머지 부분은 더 자세하게 설명하고 많은 프로그래머가 종종 실수를 일으켜 실수를 일으킨다 NullReferenceException.

더 구체적으로

runtimeA가 던지는 NullReferenceException 항상 당신이 참조를 사용하려고하고 참조가 초기화되지 않습니다 (또는이되었다 같은 일을 의미 한 번 초기화되지만, 더 이상 초기화 없음).

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

string foo = null;
foo.ToUpper();

를 가리키는 참조 NullReferenceException에서 인스턴스 메소드 ToUpper()를 호출 할 수 없기 때문에 두 번째 행 에가 표시 string됩니다 null.

디버깅

의 출처를 어떻게 찾을 수 NullReferenceException있습니까? 그 외에도이 발생하는 위치에 정확하게 던져 질 것이다 제외하고 자체보고에서, Visual Studio에서 디버깅의 일반 규칙이 적용 장소 전략적 중단 점 및 귀하의 변수를 검사 , 중 하나 (개방, 자신의 이름 위에 마우스를 가져 가면 빨리보기) 또는 지역 및 자동차와 같은 다양한 디버깅 패널을 사용하십시오.

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

이 방법으로 프로그램 흐름을 따르면 인스턴스가 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.Currentnull이거나 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; }
}

중첩 컬렉션 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.

행사

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

###Bad Naming Conventions:

If you named fields differently from locals, you might have realized that you never initialized the field. 

공개 클래스 Form1 {개인 고객 고객;

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 빈 뷰 모델

@Model에서의 속성을 참조 할 때 예외가 발생하면 뷰를 볼 때 작업 메소드에서 가져 오기가 설정되어 ASP.NET MVC View있음을 이해해야합니다 . 컨트롤러에서 빈 모델 (또는 모델 속성)을 반환하면 뷰가 액세스 할 때 예외가 발생합니다.Modelreturn

// 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시각적 트리에 나타나는 순서대로 만들어 집니다. (A)는 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목록을 작성 하여) 선언의 순서를 변경하면 적어도 여기 에서 해결 됩니다.label1comboBox1NullReferenceException

캐스트 as

var myThing = someObject as Thing;

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

LINQ FirstOrDefault()SingleOrDefault()

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

각각

foreachnull 수집을 반복하려고 할 때 발생합니다. 일반적으로 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인스턴스 멤버에 액세스하기 전에 참조가 있는지 확인할 수 있습니다 .

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

사용 Debug.Assert값이되어서는 안 경우 null문제 이전에 예외가 발생보다 잡으려고.

개발 중에 메서드가 반환 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.
}

이 검사는 비록 당신의 릴리스 빌드에서 생을 마감하지 않습니다 는 던져 원인이 NullReferenceException때 다시 book == null릴리스 모드에서 실행시.

사용 GetValueOrDefault()을위한 nullable그들이 때 값 형식은 기본값을 제공합니다 null.

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]를 사용하십시오.

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

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

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

var title = person.Title.ToUpper();

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

C# 5이하, 이것은으로 보호 할 수 있습니다 :

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

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

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

이 제목 변수의 존재가 발생합니다 null, 그리고 호출하는 ToUpper경우 제작되지 않은 person.Title것입니다 null.

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

// 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");

이 작업은 다음을 수행합니다. myIntArraynull 인 경우 표현식은 null을 반환하므로 안전하게 확인할 수 있습니다. 배열이 포함 된 경우 : elem = myIntArray[i];와 동일하게 수행 되며 i<sup>th</sup>요소를 반환합니다 .

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

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

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

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

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

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

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! 호출은 GetFrobs단순히 반복 반복자 블록을 실행할 오브젝트를 리턴합니다 .

이와 같이 null 검사를 작성하면 null 역 참조를 방지 할 수 있지만 null 인수 예외를 호출 지점이 아닌 반복 지점으로 이동하면 디버그하기매우 복잡합니다 .

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

// 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수행 된 다음 시퀀스가 반복 될 때 실행됩니다.

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

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

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

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

  • 포인터 를 역 참조하면 널 참조 를 역 참조하는 것과 동일한 예외가 발생합니다.
  • 널 (null)이 아닌 널 (null)이 아닌 포인터를 참조 해제하면 일부 상황에서 해당 예외가 발생할 수 있습니다

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

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

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

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

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

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

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


55
어쩌면 이것은 바보 같은 의견이지만이 문제를 피하는 가장 좋은 방법은 객체를 초기화하는 것입니다. 나 에게이 오류가 발생하면 일반적으로 배열 요소와 같은 것을 초기화하는 것을 잊었 기 때문입니다. 객체를 null로 정의 한 다음 참조하는 것이 훨씬 덜 일반적이라고 생각합니다. 어쩌면 설명에 인접한 각 문제를 해결할 수있는 방법을 제시하십시오. 여전히 좋은 소식입니다.
JPK

30
객체가 아니라 메소드 또는 속성의 반환 값이 있으면 어떻게됩니까?
존 손더스

6
이 책 / 저자 예제는 조금 이상합니다 ... 어떻게 컴파일 되나요? 인텔리전스는 어떻게 작동합니까? 나는 이것이 전산에 좋지 않다 ...

5
@ 윌 : 마지막 편집이 도움이 되나요? 그렇지 않은 경우 문제로 보이는 것에 대해 더 명확하게 설명하십시오.
존 손더스

6
@JohnSaunders 아, 아뇨, 죄송합니다. 객체 초기화 버전을 의미했습니다. new Book { Author = { Age = 45 } };내부 초기화는 어떻게합니까 ... 내부 초기화가 작동하는 상황을 생각할 수는 없지만 컴파일하고 지능적으로 작동합니다 ... 구조체가 아닌 한?

311

NullReference 예외 — Visual Basic

그만큼 NullReference Exceptionfor Visual BasicC # for 와 다릅니다 . 결국 둘 다 .NET Framework에 정의 된 것과 동일한 예외를보고합니다. Visual Basic에 고유 한 원인은 거의 없습니다 (아마도 하나만).

이 답변은 Visual Basic 용어, 구문 및 컨텍스트를 사용합니다. 사용 된 예제는 과거의 많은 스택 오버플로 질문에서 비롯된 것입니다. 이는 다음을 사용하여 관련성을 극대화하기위한 것입니다. 종류의 자주 게시물에서 볼 상황을. 그것을 필요로하는 사람들을위한 약간의 설명도 제공됩니다. 당신과 비슷한 예가되어 매우 가능성이 여기에 나열된.

노트 :

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

기본 의미

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

  • 코드 에서 객체 변수를 선언 했지만 초기화 하지 않았습니다 (인스턴스 생성 또는 ' 인스턴스화 ').
  • 코드에서 객체를 초기화한다고 가정했지만
  • 다른 코드가 아직 사용중인 객체를 조기에 무효화했을 수 있습니다.

원인 찾기

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

IDE 디버그 표시

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

MsgBox캐치에 표시되는 A Error while...는 거의 도움이되지 않습니다. 이 방법은 또한 매우 나쁜 실제 예외, 관련된 객체 또는 발생하는 코드 라인을 설명 할 수 없기 때문에 스택 오버플로 질문으로 집니다.

당신은 또한 사용할 수 있습니다 Locals Window( > 지방 주민 - -> 윈도우 디버그 개체를 검사).

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

또한보십시오:

예와 구제

클래스 객체 / 인스턴스 만들기

Dim reg As CashRegister
...
TextBox1.Text = reg.Amount         ' NRE

문제는 DimCashRegister 오브젝트를 작성하지 않는 것입니다 . reg해당 유형 의 변수 만 선언합니다 . 객체 변수 선언인스턴스 생성 은 서로 다른 두 가지입니다.

치료제

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

참고 : 생성자 ( )를 포함하여 프로 시저에서 다시 사용 하지 마십시오 .DimSub New

Private reg As CashRegister
'...

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

그러면 해당 컨텍스트 (sub)에만 존재 하는 로컬 변수 가 생성됩니다 reg. 다른 곳에서 사용할 reg모듈 수준 의 변수는 Scope그대로 유지 Nothing됩니다.

New연산자가 누락 된 원인은 # 1입니다.NullReference Exceptions 검토 된 스택 오버플로 질문에서 가장 큰 .

Visual Basic의 프로세스가 사용하여 반복적으로 명확하게하려고 New다음 사용 New운영자 것은 만들어 새로운 객체와 통화 Sub New- 생성자 - 개체가 다른 초기화를 수행 할 수 있습니다.

명확히하기 위해 Dim(또는 Private) 변수와 변수 만 선언 합니다 Type. 변수 의 범위 ( 전체 모듈 / 클래스에 존재하는지 또는 프로 시저에 로컬인지) 는 선언 된 위치에 따라 결정됩니다 . Scope가Private | Friend | Public 아닌 액세스 레벨을 정의합니다 .

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


배열

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

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 InferAs <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)클래스를 참조하십시오 .


데이터 제공자 객체

NullReference에 대한 데이터베이스를 선물로 많은 기회를 작업 많은 개체 (이있을 수 있기 때문에 Command, Connection, Transaction, Dataset, DataTable, DataRows한 번에 사용 ....). 참고 : MySQL, SQL Server, OleDB 등 어떤 데이터 공급자를 사용하든 개념 은 동일하지 않습니다.

실시 예 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

이전과 마찬가지로 dsDataset 객체가 선언되었지만 인스턴스가 생성되지 않았습니다. 는 생성하지 않고 DataAdapter기존을 채 웁니다 DataSet. 이 경우, ds로컬 변수이므로 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)

오타 여기에 문제가 : EmployeesEmployee. DataTable"직원"이라는 이름의 이름 이 작성되지 않았 으므로 NullReferenceException결과에 액세스하려고했습니다. 또 다른 잠재적 인 문제는 ItemsSQL에 WHERE 절이 포함되어 있지 않을 수도 있다는 가정 입니다.

치료제

이것은 하나의 테이블을 사용 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

FillRows테스트 할 수있는 영향을받는 수를 반환하는 함수입니다 .

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

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

해결책 인덱스 테이블을 참조 동일하다 :

If ds.Tables(0).Rows.Count > 0 Then

DataTable 클래스 도 참조하십시오 .


객체 경로 / 중첩

If myFoo.Bar.Items IsNot Nothing Then
   ...

코드는 테스트하고 Items모두 동안 myFooBar도 아무것도 할 수있다. 해결책은 한번에 전체 쇄상 또는 물체 중 하나의 경로를 테스트 할 수있다 :

If (myFoo IsNot Nothing) AndAlso
    (myFoo.Bar IsNot Nothing) AndAlso
    (myFoo.Bar.Items IsNot Nothing) Then
    ....

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

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

null객체 의 '다운 스트림'을 참조하는 것은 불가능 합니다. 이것은 컨트롤에도 적용됩니다.

myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"

여기에 myWebBrowser또는 Document아무것도 없거나 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.ToStringNRE가됩니다.

치료제

사용하기 전에 데이터의 유효성을 검사하십시오 ( 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 전용 상황입니다. 또한 캐스케이드가 실패 할 수 있으므로 복잡합니다.

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

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

나중에 배열 요소를 참조하면 NRE가 발생합니다. Form_Load이상한 버그로 인해이 작업을 수행 하면 IDE 에서 예외가 발생했을 때보고 하지 않을 수 있습니다 . 나중에 코드에서 배열을 사용하려고하면 예외가 나타납니다 . 이 "자동 예외"는 이 게시물에서 자세히 설명 합니다. 우리의 목적을 위해, 핵심은 폼 ( 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

참고 이 어떤 적용 및 모든 제어 및 구성 요소 참조 그들이 어디에 이러한 불법을 :

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가 경고를 제공하지 않는 것이 궁금하지만 해결 방법은 컨테이너를 양식 레벨에서 선언 하는 것이지만 컨트롤 존재 하면 양식로드 이벤트 핸들러에서 컨테이너를 초기화 하는 것입니다 . 코드가 호출 후에 만 수행 할 수 있습니다 .Sub NewInitializeComponent

' 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는 당신에게 길의 오류를 보여줍니다

"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 = FalseNRE가 발생할 때 리턴을 사용하려고하는 모든 것 :

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

치료제

Exit Function함수에서로 교체하십시오 Return bList. 비어있는 것을 반환하는 List것은 반환하는 것과 다릅니다 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

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

SQL에 여분의 쉼표가 있으며 ( 'mailaddress'뒤)에 예외가 발생 .ExecuteReader합니다. (가) 후 Catch아무것도하지 않는다, Finally정리를 수행하려고하지만, 수 없습니다 이후 CloseDataReader개체, 새로운 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함수는 이 같은지 테스트하는 데 사용됩니다 System.DBNull. 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

경우 CheckBoxchkName찾을 수 없습니다 (또는이 존재 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사실이든 아니든 작동합니다 .

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

For n루프를 사용하는 경우 행 수를 수정하거나 true 일 Exit For때 사용하십시오 IsNewRow.


내 설정 (StringCollection)

특정 상황에서 항목 사용하려고 My.Settingsa가되는 StringCollectionNullReference에 당신이 그것을 처음 사용할 때 발생할 수 있습니다. 해결책은 동일하지만 명확하지 않습니다. 치다:

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 예외


226

또 다른 시나리오는 널 오브젝트를 값 유형 으로 캐스트 할 때 입니다. 예를 들어 아래 코드는 다음과 같습니다.

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

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

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

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

여기에, SelectedDate의 - 속성 사실상 DateTime종류 -의 Calendar웹 컨트롤 유형 및 완벽하게 뭔가 널 (null)을 반환 할 수 바인딩. 암시 적 ASP.NET 생성기는 위의 캐스트 코드와 동등한 코드를 만듭니다. 그리고 이것은 NullReferenceException잘 컴파일되는 ASP.NET 생성 코드에 있기 때문에 발견하기가 매우 어렵습니다 ...


7
대단한 캐치. 피하는 한 줄짜리 방법 :DateTime x = (DateTime) o as DateTime? ?? defaultValue;
Shultz Serges

159

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

SqlConnection connection = null;
connection.Open();

변수 " connection"를 선언했지만 아무 것도 가리 키지 않기 때문에 오류가 발생 합니다. 회원에게 전화를 걸 때 "Open " 이를 해결할 참조가 없으며 오류가 발생합니다.

이 오류를 피하려면

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

JetBrains의 Resharper 도구는 코드에서 null 참조 오류가 발생할 가능성이있는 모든 위치를 식별하여 null 검사를 수행 할 수 있습니다. 이 오류는 버그의 가장 큰 원인 인 IMHO입니다.


3
JetBrains의 Resharper 도구는 null 참조 오류 가능성이있는 코드의 모든 위치를 식별합니다. 이것은 올바르지 않습니다. 그 탐지가없는 해결책이 있지만 때로는 코드에서 예외가 발생합니다. 멀티 스레딩이 관련되어있을 때 가끔은 감지 할 수없는 것으로 의심되지만 아직 버그 위치를 식별하지 못했기 때문에 더 이상 언급 할 수 없습니다.
j riv

그러나 NullReferenceException이 HttpContext.Current.Responce.Clear ()를 사용할 때 해결하는 방법. 위의 솔루션으로 해결되지 않습니다. HttpContext의 객체 객체를 생성하는 동안 "액세스 가능한 '신규'가이 개수의 인수를 허용하지 않기 때문에 과부하 해결에 실패했습니다.
Sunny Sandeep

157

이는 코드에서 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
}

96

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

값이 Nothing/ 인 참조 변수를 사용하려고합니다 null. 값이 참조 변수에 대해 Nothing/ 인 null경우 힙에 존재하는 오브젝트의 인스턴스에 대한 참조를 실제로 보유하고 있지 않음을 의미합니다.

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


87

이 예외가 발생하는 예는 다음과 같습니다. 무언가를 확인하려고하면 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을 발생시킵니다.

메소드가 전달되는 것이 널 (null)이 아닌 것으로 예상하는 경우 일반적으로 방어 조치로 발생하는 ArgumentNullException과 비교합니다.

자세한 내용은 C # NullReferenceException 및 Null Parameter에 있습니다.


87

C # 8.0, 2019 업데이트 : Nullable 참조 유형

C # 8.0은 널 입력 가능 참조 유형널 입력 불가능 참조 유형을 도입 합니다 . 따라서 NullReferenceException 을 피하려면 널 입력 가능 참조 유형 만 점검해야합니다. .


참조 유형을 초기화하지 않고 해당 속성 중 하나를 설정하거나 읽으려는 경우 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이 발생하는 이유를 완전히 이해하려면 값 유형 과 [참조 유형] [3] 의 차이를 아는 것이 중요합니다 .

따라서 값 유형을 처리하는 경우 NullReferenceExceptions가 발생할 수 없습니다 . 참조 유형을 다룰 때주의를 기울여야 합니다 !

이름에서 알 수 있듯이 참조 유형 만 참조를 보유하거나 문자 그대로 아무 것도 가리킬 수 없습니다 (또는 '널'). 반면에 값 유형에는 항상 값이 포함됩니다.

참조 유형 (이 유형을 확인해야 함) :

  • 동적
  • 목적

값 유형 (단순히 무시할 수 있음) :

  • 숫자 형
  • 일체형
  • 부동 소수점 유형
  • 소수
  • 부울
  • 사용자 정의 구조체

6
-1 : 질문은 "NullReferenceException이란 무엇입니까"이므로 값 유형은 관련이 없습니다.
John Saunders

21
@ 존 손더스 : 동의하지 않습니다. 소프트웨어 개발자는 가치와 참조 유형을 구분할 수 있어야합니다. 그렇지 않으면 사람들은 정수가 null인지 확인하게됩니다.
Fabian Bigler

5
사실,이 질문의 맥락에서 아닙니다.
존 손더스

4
힌트 주셔서 감사합니다. 나는 그것을 조금 개선하고 상단에 예를 추가했습니다. 여전히 참조 및 값 유형을 언급하는 것이 유용하다고 생각합니다.
Fabian Bigler 2013 년

5
질문에 참조 유형이 미리 지정되어 있기 때문에 다른 답변에없는 것을 추가하지 않았다고 생각합니다.
John Saunders

78

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호환되지 않는 유형이다; a Car로 변환 / 캐스트 할 수 없습니다 Book. 이 캐스트가 실패하면를 as반환합니다 null. mybook이 후 사용 하면NullReferenceException .

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

타입 변환이 항상 성공할 것으로 기대한다면 (즉, 객체가 무엇보다 앞서야하는지 알고 있다면) 캐스트를 사용해야합니다.

ComicBook cb = (ComicBook)specificBook;

이 유형의 알 수없는, 그러나 당신이 원하는 경우에 시도 특정 유형으로 사용하기 위해, 다음 사용 as:

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

2
변수를 언 박싱 할 때 이런 일이 많이 발생할 수 있습니다 . UI 요소의 유형을 변경 한 후 이벤트 처리기에서 자주 발생하지만 코드 숨김을 업데이트하는 것을 잊어 버렸습니다.
Brendan

65

널값 참조가 포함 된 오브젝트를 사용하고 있습니다. 따라서 null 예외가 발생합니다. 이 예에서 문자열 값은 null이며 길이를 확인할 때 예외가 발생했습니다.

예:

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

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

처리되지 않은 예외 :

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


1
얼마나 심오한가! 나는 'null'상수를 참조 값으로 고려하지 않았습니다. 그래서 이것은 C #에서 "NullPointer"를 추상화하는 방법입니다. C ++에서 기억할 때 B / c, 초기화되지 않은 포인터 (예 : c #의 ref 유형)를 역 참조하면 NPE가 발생할 수 있습니다.이 값은 기본값이 해당 프로세스에 할당되지 않은 주소입니다 (많은 경우 0, 특히 자동 초기화를 수행 한 C ++의 이후 버전에서는 OS에 속하는 -f와 함께 사용하고 beeotch로 죽습니다 (또는 OS가 프로세스를 공격하는 sigkill을 잡습니다).
samis

64

하지만 무엇 원인 NullReferenceExceptions을 하고 접근 을 피 / 수정 되었습니다는 다른 답변에서 해결 이러한 예외를, 많은 프로그래머가 아직 배운하지 않은 무슨 일이 얼마나 독립적으로 디버깅 개발하는 동안 같은 예외를.

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


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

그런 다음 디버깅 시작 (F5) 또는 [VS 디버거]를 실행중인 프로세스에 연결하십시오 . 때때로을 사용하는 것이 유용 할 수 있으며 Debugger.Break, 이는 디버거를 시작하라는 프롬프트를 표시합니다.

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

예를 들어, 다음 행 에서 예외를 발생 시킬 수 있는 유일한 코드는 myStringnull로 평가되는 경우 입니다. 이것은보고 확인할 수 있습니다 조사 식 창 또는 표현식 실행 직접 실행 창을 .

var x = myString.Trim();

다음과 같은 고급 경우에는 위의 기술 중 하나 (Watch 또는 Immediate Windows)를 사용하여식이 str1Null인지 Null인지 확인해야 str2합니다.

var x = str1.Trim() + str2.Trim();

일단 여기서 예외가 던져가있는 한, 그것은 널 값이 어디에 있는지 알아 이유 뒤쪽에 보통 사소한 [잘못] 소개 -

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


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

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


59

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

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

여기서 개봉기 변환 (캐스트) 로부터 object (또는 클래스 중 하나 System.ValueType또는 System.Enum또는 인터페이스 유형)에서 이외의 값 타입 (Nullable<> 자체)를 제공NullReferenceException .

다른 방향에서, 권투 변환 에서Nullable<> 가지고 HasValuefalse 참조 형은 줄 수 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.

이러한 사례는 Nullable<>인스턴스를 복싱 할 때 런타임에서 사용하는 특수 규칙으로 인해 발생 합니다.


42

엔티티 프레임 워크에서 사용되는 엔티티의 클래스 이름이 웹 양식 코드 숨김 파일의 클래스 이름과 동일한 경우를 추가하십시오.

코드 숨김 클래스가 Contact 인 웹 양식 Contact.aspx가 있고 엔터티 이름이 Contact이라고 가정합니다.

그런 다음 context를 호출하면 다음 코드에서 NullReferenceException이 발생합니다 .SaveChanges ()

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을 살펴보십시오.


41

이 예외가 발생할 수있는 또 다른 일반적인 경우는 단위 테스트 중에 클래스를 조롱하는 것입니다. 사용되는 조롱 프레임 워크에 관계없이 모든 적절한 수준의 클래스 계층이 제대로 조롱되었는지 확인해야합니다. 특히, 모든 속성HttpContext 테스트중인 코드에서 참조하는 조롱해야합니다.

다소 자세한 예제는 " 사용자 정의 AuthorizationAttribute 테스트시 NullReferenceException 발생 "을 참조하십시오 .


40

나는 이것에 대답하는 데 다른 관점을 가지고 있습니다. 이런 종류의 대답은 "어떻게 피할 수 있습니까? "

MVC 애플리케이션과 같이 서로 다른 계층 에서 작업 할 때 컨트롤러는 비즈니스 운영을 호출하는 서비스가 필요합니다. 이러한 시나리오에서는 Dependency Injection Container 를 사용하여 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();
    }
}

6
-1 : 초기화되지 않은 종속성의 단일 시나리오 만 처리합니다. 이것은 NullReferenceException에 대한 소수 시나리오입니다. 대부분의 경우 객체의 작동 방식에 대한 간단한 오해입니다. 다음으로 가장 빈번한 것은 개발자가 객체가 자동으로 초기화 될 것이라고 가정 한 상황입니다.
John Saunders

의존성 주입은 일반적으로 NullReferenceException을 피하기 위해 사용되지 않습니다. 나는 당신이 여기서 일반적인 시나리오를 찾았다 고 믿지 않습니다. 어쨌든 stackoverflow.com/a/15232518/76337 스타일로 답변을 편집 하면 downvote를 제거합니다.
존 손더스

38

의 문제에 "나는 그것에 대해 무엇을해야" 많은 답변이있을 수 있습니다.

개발 중에 이러한 오류 조건 방지하는보다 "공식적인"방법은 코드에서 계약 에 따라 디자인을 적용 하는 것 입니다 . 즉 , 개발하는 동안 클래스 불변 값을 설정 하거나 시스템의 기능 / 메소드 전제 조건사후 조건을 설정해야합니다.

요컨대, 클래스 불변 은 클래스에 정상적인 사용에서 위반되지 않는 제약이 있는지 확인합니다 (따라서 클래스가 일관성이없는 상태에 있지 않음 ). 전제 조건 은 함수 / 메소드에 대한 입력으로 제공된 데이터가 일부 제한 조건 세트를 따라야하며 이를 위반 하지 않아야 함을 의미하며 , 사후 조건 은 함수 / 메소드 출력이 설정된 제한 조건을 다시 위반하지 않고 다시 설정 제한 조건을 따라야 함을 의미합니다. 계약 조건은해야 결코 그러므로되는 동안, 디버그 모드에서 실제로 체크 계약에 의해 설계, 버그없는 프로그램을 실행하는 동안 위반되지 릴리스에서 비활성화 개발 된 시스템 성능을 최대화하기 위해.

이런 식으로 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 );
    //...
}

이로 인해 .NET 애플리케이션에 대한 Code Contracts 프로젝트가 존재합니다.

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

업데이트 : 이 용어는 에펠 프로그래밍 언어의 디자인과 관련하여 Bertrand Meyer 의해 만들어졌습니다 .


2
나는 이것을 언급하지 않은 것처럼 이것을 추가하려고 생각했으며, 그것이 접근 방식으로 존재하는 한, 나의 의도는 주제를 풍부하게하는 것이 었습니다.
Nick Louloudakis 2014

2
주제를 풍부하게 해주셔서 감사합니다. 나는 당신의 추가에 대한 나의 의견을 주었다. 이제 다른 사람들도 그렇게 할 수 있습니다.
John Saunders

2
나는 이것이 높은 관점의 스레드라는 것을 감안할 때 이것이 주제에 가치있는 추가라고 생각했습니다. 코드 계약에 대해 들어 본 적이 있는데이를 사용하는 것이 좋습니다.
VoteCoffee

36

NullReferenceExceptionnull 객체의 속성에 액세스하려고하거나 문자열 값이 비어 있고 문자열 메서드에 액세스하려고하면 A 가 발생합니다.

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

  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 

2
이것은 올바르지 않습니다. String.Empty.ToLower()null 참조 예외를 throw하지 않습니다. 빈 문자열 임에도 불구하고 실제 문자열을 나타냅니다 (예 :) "". 여기에는 호출 할 객체가 ToLower()있으므로 null 참조 예외를 throw하는 것은 의미가 없습니다.
Kjartan

31

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


다음 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 가끔 오류가 보이는 구불 구불 한 선을 던졌습니다에서 Html.Partial그것이 다르게 구성 안에 있다면 foreach정말 오류가 아니더라도, 루프 :

@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가 앰퍼샌드와 괄호를 잘못 읽고 있기 때문에 느낌이 들었습니다.


당신은 싶어 Html.Partial하지@Html.Partial
존 손더스에게

또한 어떤 줄에서 예외가 발생했는지와 그 이유를 보여주십시오.
존 손더스

MyOtherView.cshtml에서 오류가 발생했습니다. 여기에 포함되지 않았습니다. 모델이 제대로 전송되지 않았기 때문에 ( Null그렇습니다), 모델을 전송하는 방법에 오류가 있음을 알았습니다.
Travis Heeter

22

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

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

인수 확인

예를 들어, 메소드는 다른 인수가 널인지 확인 ArgumentNullException하고이 목적을 위해 분명히 작성된 예외 인를 던질 수 있습니다.

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 Code Contracts"가 있습니다 : Introducing Code Contracts .

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

public void DoSometing([NotNull] obj)

그렇게하면 빌드 프로세스의 PostSharp 부분을 obj런타임에 null로 검사합니다. 참조 : PostSharp 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 };
    }
}

Nullable<T>허용하지 않기 위해 정확히 반대의 목표를 달성한다는 점을 제외하고 는 사용하는 것과 같은 방식으로 매우 유사하게 사용할 것입니다 null. 여기 몇 가지 예가 있어요.

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므로 필요한 곳 ​​어디에서나 사용할 수 있습니다. 예를 들어 Person객체를 다음과 같은 메소드로 전달할 수 있습니다 NotNull<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에서 위의 코드를 사용할 수 있도록 만들었습니다.

https://github.com/luisperezphd/NotNull

관련 언어 기능

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

이를 통해 일부 경우에 수행해야하는 널 검사 수가 줄어 듭니다. 구문은 각 점 앞에 물음표를 넣는 것입니다. 예를 들어 다음 코드를 사용하십시오.

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

그것이 속성이라고 불리는 country유형의 객체 라고 상상해보십시오 . 만약 , , , 또는 이다 다음 널 (null) 주소 null`.CountryStatecountryStateCountyCitynulladdress will be. Therefore you only have to check whetheris

훌륭한 기능이지만 정보를 덜 제공합니다. 4 중 어느 것이 null인지는 분명하지 않습니다.

Nullable처럼 내장되어 있습니까?

C #은 축약 형입니다 . Nullable<T>형식 뒤에 물음표를 붙여서 Null을 만들 수 있습니다 int?.

C #에 NotNull<T>위 의 구조체와 비슷한 것이 있고 느낌표 (!)와 비슷한 속기가 있으면 다음과 같이 쓸 수 public void WriteName(Person! person)있습니다.


2
NullReferenceException을 던지지 마십시오
John Saunders

@JohnSaunders 감히 내가 왜 물어? (심하게 왜 그런가?)
Luis Perez

2
NullReferenceException은 CLR에서 발생합니다. 널에 대한 참조가 발생했음을 의미합니다. 그것은 당신이 영리하게 먼저 확인하는 것을 제외하고는 null에 대한 참조가 발생한다는 것을 의미하지는 않습니다.
John Saunders

혼란스러운 점에 대한 당신의 요점을 봅니다. 이 예제의 일반적인 예외와 GitHub의 사용자 정의 예외로 업데이트했습니다.
Luis Perez

이러한 기본적인 질문에 대한 훌륭한 답변. 그것이 실패한 코드 일 때 그렇게 나쁘지 않습니다. 의존하고있는 상용 써드 파티 라이브러리에서 깊게 들어 오면 끔찍한 일이며, 고객 지원팀은 문제의 원인이되는 코드 여야한다고 주장합니다. 그리고 당신은 그것이 확실하지 않다고 확신하지 못하고 전체 프로젝트가 중단 될 것입니다. 나는 실제로 이것이 묘비에 대한 적절한 표현이 될 것이라고 생각합니다.
Darrel Lee

10

흥미롭게도,이 페이지의 어떤 답변도 두 가지 경우를 언급하지 않습니다.

에지 사례 # 1 : 사전에 동시 액세스

.NET의 일반 사전은 스레드로부터 안전하지 않으며 두 개의 동시 스레드에서 키에 액세스하려고 할 때 때때로NullReference (더 자주) a를 던질 수 있습니다 KeyNotFoundException. 이 경우 예외는 오해의 소지가 있습니다.

에지 사례 # 2 : 안전하지 않은 코드

코드에 NullReferenceException의해 a 가 발생 unsafe하면 포인터 변수를보고 그 변수를 확인하십시오 IntPtr.Zero. 어느 것이 똑같지 만 ( "널 포인터 예외") 안전하지 않은 코드에서는 변수가 종종 값 유형 / 배열 등에 캐스트되고, 값 유형이 어떻게 이것을 던질 수 있을지 궁금해 벽에 머리를 부딪칩니다. 예외.

(필요하지 않은 한 안전하지 않은 코드를 사용하지 않는 또 다른 이유)


5
사전 예제는 대소 문자가 아닙니다. 객체가 스레드로부터 안전하지 않은 경우 여러 스레드에서 객체를 사용하면 임의의 결과가 생성됩니다. 안전하지 않은 코드 예제는 null어떤 면에서 다른 가요?
John Saunders

10

c # 6의 Null 조건부 연산자를 사용하여 NullReferenceException을 깔끔하게 수정하고 적은 수의 코드를 작성하여 null 검사를 처리 할 수 ​​있습니다.

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

  var name = p?.Spouse?.FirstName;

다음과 같습니다.

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

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

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

자세한 내용 : Null 조건부 연산자


9

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

예를 들어, 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;  
     }
}

4

사용하려는 클래스의 객체가 인스턴스화되지 않은 경우 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

간단히 말해서,

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

그래서 이것을 해결하는 방법 :

  1. 디버그하고 디버거를 중단시킵니다 ... 직접 깨진 변수로 이동합니다 ... 이제 작업을 간단히 수정 하는 것입니다. 적절한 위치에서 키워드를 사용하십시오 .

  2. 오브젝트가 없기 때문에 일부 데이터베이스 명령 에서 발생한 경우 널 점검을 수행하고 처리하기 만하면됩니다.

    if (i == null) {
        // Handle this
    }
  3. 가장 어려운 것은 .. GC가 이미 개체를 수집 한 경우 ... 이것은 일반적으로 문자열을 사용하여 개체를 찾으려고 할 때 발생합니다. 즉, 개체 이름으로 개체를 찾은 경우 GC가 이미 발생할 수 있습니다. 정리하기 ... 찾기가 어렵고 상당히 문제가 될 것입니다 ... 이것을 해결하는 더 좋은 방법은 개발 과정에서 필요한 곳 ​​어디에서나 널 검사를하는 것입니다. 이렇게하면 많은 시간이 절약됩니다.

이름으로 찾음에 따라 일부 프레임 워크에서 문자열을 사용하여 FIndObject를 찾을 수 있으며 코드는 다음과 같습니다. FindObject ( "ObjectName");


3
객체에 대한 참조가있는 경우 GC는이를 정리하지 않습니다.
John Saunders

2
당신이 FindObject ( "개체의 이름") 등을 사용하는 경우 방법의 GC는 refernece 해당 객체에가는 것을 손 전에 알아야하지 않습니다이 ..이 다음 실행시에 발생 .. explaing려고 무엇이다
아카 쉬 Gutha

2
Unity와 같은 C #에서이 기능을 제공하는 일부 프레임 워크가 있습니다. 질문은 BCl과 관련이 없습니다. 비판하기 전에 인터넷을 검색하십시오. 많은 기능이 있으며, 정보를 찾기 위해 매일 사용합니다. 이제 대답이 어떻습니까?
Akash Gutha

2
docs.unity3d.com/ScriptReference/… 링크를 확인하고 자신을 수정하십시오 mr.expert : p
Akash Gutha

귀하의 링크에서 본 예제는 GameObject.Find의 결과를 멤버 필드에 할당합니다. 그것은 참조이며 GC는 포함 객체가 수집 될 때까지 그것을 수집하지 않습니다.
John Saunders

1

말 그대로 NullReferenceExeption을 수정하는 가장 쉬운 방법은 두 가지 방법이 있습니다. 예를 들어 스크립트가 첨부 된 게임 오브젝트와 rb (rigidbody)라는 변수가있는 경우 게임을 시작할 때이 변수는 null로 시작합니다.
컴퓨터에 해당 변수에 저장된 데이터가 없기 때문에 NullReferenceExeption을 얻는 이유입니다.

예를 들어 RigidBody 변수를 사용하겠습니다.
실제로 몇 가지 방법으로 데이터를 실제로 쉽게 추가 할 수 있습니다.

  1. AddComponent> Physics> Rigidbody를 사용하여 개체에 RigidBody를 추가 한
    다음 스크립트로 이동하여 입력하십시오. rb = GetComponent<Rigidbody>();
    이 코드 줄은 사용자 Start()또는 Awake()함수에서 가장 잘 작동 합니다.
  2. 프로그래밍 방식으로 컴포넌트를 추가하고 한 줄의 코드로 변수를 동시에 지정할 수 있습니다. rb = AddComponent<RigidBody>();

추가 참고 사항 : 개체에 구성 요소를 추가하고 단일 구성 요소를 추가 [RequireComponent(typeof(RigidBody))]하지 않은 경우 클래스 선언 이상 (모든 사용 아래의 공간)을 입력 할 수 있습니다 .
즐기고 재미있는 게임 만들기!


-1

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

전의:

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

-3

이것은 기본 적으로 Null 참조 예외 입니다. Microsoft는 다음과 같이 말합니다.

값이 null 인 유형의 멤버에 액세스하려고하면 NullReferenceException 예외가 발생합니다.

그게 무슨 뜻이야?

즉, 가치가없는 회원이 있고 해당 회원이 특정 작업을 수행하도록하는 경우 시스템은 의심의 여지없이 메시지를 던져

"잠깐만 요, 그 멤버는 가치가 없어서 당신이 맡고있는 작업을 수행 할 수 없습니다."

예외 자체는 무언가가 참조되고 있지만 값이 설정되지 않았다고 말합니다. 따라서 이는 값 유형이 널 입력 불가능하므로 참조 유형을 사용하는 동안에 만 발생 함을 나타냅니다.

Value 유형 멤버를 사용하는 경우 NullReferenceException이 발생하지 않습니다.

class Program
{
    static void Main(string[] args)
    {
        string str = null;
        Console.WriteLine(str.Length);
        Console.ReadLine();
    }
}

위의 코드는 null 값 이 할당 된 간단한 문자열을 보여줍니다 .

지금, 문자열의 길이를 인쇄 할 때 STR을 , 나는 어떻게해야합니까 'System.NullReferenceException'형식의 처리되지 않은 예외가 발생 멤버 때문에 메시지를 STR은 null로 지적되고 널의 길이있을 수 없다.

' NullReferenceException '은 참조 유형의 인스턴스화를 잊었을 때도 발생합니다.

클래스와 멤버 메소드가 있다고 가정하십시오. 클래스를 인스턴스화하지 않았지만 클래스 이름 만 지정했습니다. 이제 메서드를 사용하려고하면 컴파일러에 따라 오류가 발생하거나 경고가 발생합니다 (컴파일러에 따라 다름).

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");

    }
}

위 코드의 컴파일러는 변수 obj 가 할당되지 않았다는 오류를 발생시킵니다. 이는 변수에 null 값이 있거나 없음을 나타냅니다. 위 코드의 컴파일러는 변수 obj 가 할당되지 않았다는 오류를 발생시킵니다. 이는 변수에 null 값이 있거나 없음을 나타냅니다.

왜 발생합니까?

  • NullReferenceException은 객체 값을 확인하지 않은 오류로 인해 발생합니다. 우리는 종종 코드 개발에서 객체 값을 체크하지 않은 채로 둡니다.

  • 객체를 인스턴스화하는 것을 잊었을 때도 발생합니다. null 값을 반환하거나 설정할 수있는 메서드, 속성, 컬렉션 등을 사용하는 것도이 예외의 원인 일 수 있습니다.

어떻게 피할 수 있습니까?

이 예외를 피할 수있는 다양한 방법과 방법이 있습니다.

  1. 명시 적 검사 : 개체, 속성, 메서드, 배열 및 컬렉션이 null인지 여부를 확인하는 전통을 준수해야합니다. if-else if-else 등과 같은 조건문을 사용하여 간단하게 구현할 수 있습니다.

  2. 예외 처리 :이 예외를 관리하는 중요한 방법 중 하나입니다. 간단한 try-catch-finally 블록을 사용하여이 예외를 제어하고 로그를 유지할 수도 있습니다. 응용 프로그램이 프로덕션 단계에있을 때 매우 유용합니다.

  3. Null 연산자 : Null Coalescing 연산자 및 Null 조건부 연산자를 사용하여 값을 개체, 변수, 속성 및 필드로 설정할 수 있습니다.

  4. 디버거 : 개발자에게는 큰 디버깅 무기가 있습니다. 개발 단계에서 NullReferenceException에 직면 한 경우 디버거를 사용하여 예외 소스에 도달 할 수 있습니다.

  5. 내장 메소드 : GetValueOrDefault (), IsNullOrWhiteSpace () 및 IsNullorEmpty ()와 같은 시스템 메소드는 널을 점검하고 널값이있는 경우 기본값을 지정합니다.

이미 많은 좋은 답변이 있습니다. 내 블로그의 예제를 통해 자세한 설명을 확인할 수도 있습니다 .

이것이 도움이되기를 바랍니다!


기본적으로 블로그 게시물의 절반을 복사하고 기존 답변으로 해결할 수없는 새로운 내용은 추가하지 않았습니다.
CodeCaster

@codecaster 자신의 블로그에서 요약을 다시 작성할 때 복사한다고합니다. 나는 내 대답에는 새로운 것이 없으며 이전 대답에는없는 새로운 것이 없지만 더 정교한 방식으로 기여하고 다른 사람들이 내가 이해 한 방식을 이해하도록하고 싶습니다. 한 사람에게 도움이 되더라도 기쁠 것입니다. 성실하게.
Wasim

-4

빌드를 저장하거나 컴파일하는 동안이 메시지가 표시되면 모든 파일을 닫은 다음 파일을 열어 컴파일하고 저장하십시오.

나에게 그 이유는 파일 이름을 바꾸고 이전 파일이 여전히 열려 있기 때문입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.