지역 변수에 초기화가 필요한 이유는 무엇입니까?
내 수업 내에서 부울을 만들면과 같은 bool check
것이 기본적으로 false로 설정됩니다.
bool check
클래스 내에서가 아닌 내 메소드 내에서 동일한 부울을 만들면 "할당되지 않은 로컬 변수 검사 사용"오류가 발생합니다. 왜?
유발과 다윗의 대답은 기본적으로 정확합니다. 합산:
- 할당되지 않은 지역 변수를 사용하는 것은 버그 일 가능성이 높으며, 이는 컴파일러가 저렴한 비용으로 감지 할 수 있습니다.
- 할당되지 않은 필드 또는 배열 요소를 사용하면 버그가 발생할 가능성이 적으며 컴파일러에서 조건을 감지하기가 더 어렵습니다. 따라서 컴파일러는 필드에 초기화되지 않은 변수의 사용을 감지하려고 시도하지 않고 프로그램 동작을 결정 론적으로 설정하기 위해 기본값에 대한 초기화에 의존합니다.
David의 답변에 대한 의견자는 정적 분석을 통해 할당되지 않은 필드의 사용을 감지 할 수없는 이유를 묻습니다. 이것이이 답변에서 확장하고 싶은 요점입니다.
먼저, 로컬 또는 다른 변수의 경우 실제로 변수가 할당되었는지 또는 할당되지 않았는지 정확하게 판단하는 것은 불가능합니다 . 치다:
bool x;
if (M()) x = true;
Console.WriteLine(x);
"x가 할당 되었습니까?"라는 질문 "M ()이 true를 반환합니까?"와 같습니다. 이제 Fermat의 Last Theorem이 eleventy gajillion보다 작은 모든 정수에 대해 true이면 M ()이 true를 리턴하고 그렇지 않으면 false를 리턴한다고 가정하십시오. x가 확실히 할당되었는지 확인하려면 컴파일러는 본질적으로 Fermat의 마지막 정리 증명을 생성해야합니다. 컴파일러는 그렇게 똑똑하지 않습니다.
그래서 컴파일러는 지역 주민 대신하는 일은 구현하는 것입니다 알고리즘 신속 하고 과대 평가 지역이 확실히 할당되지 않은 경우. 즉, 그것은 당신과 내가 알고 있음에도 불구하고 "이 지역이 할당되었음을 증명할 수 없습니다"라는 잘못된 긍정을 가지고 있습니다. 예를 들면 다음과 같습니다.
bool x;
if (N() * 0 == 0) x = true;
Console.WriteLine(x);
N ()이 정수를 리턴한다고 가정하십시오. 당신과 나는 N () * 0이 0이라는 것을 알고 있지만 컴파일러는 그것을 모른다. (참고 : C # 2.0 컴파일러 는 그것을 알고 있었지만 사양 에서 컴파일러가 알고 있다고 말하지 않기 때문에 최적화를 제거 했습니다.)
좋아요, 지금까지 우리는 무엇을 알고 있습니까? 현지인이 정확한 답변을 얻는 것은 비현실적이지만, 할당되지 않은 비용을 싸게 과대 평가하고 "불명확 한 프로그램을 수정하게"하는 측면에서 잘못된 결과를 얻을 수 있습니다. 잘 됐네요 필드에 대해 동일한 작업을 수행하지 않는 이유는 무엇입니까? 즉, 값을 과대 평가하는 명확한 할당 검사기를 만드는가?
글쎄, 로컬을 초기화하는 방법은 몇 가지입니까? 메소드의 텍스트 내에서 지정할 수 있습니다. 메소드 텍스트에서 람다 내에 할당 될 수 있습니다. 해당 람다는 호출되지 않을 수 있으므로 이러한 할당은 관련이 없습니다. 또는 메소드에 "out"으로 전달하여 메소드가 정상적으로 리턴 될 때 지정되었다고 가정 할 수 있습니다. 이것들은 로컬이 할당되는 매우 명확한 지점이며 로컬이 선언 된 것과 동일한 방법으로 바로 거기에 있습니다. 현지인에 대한 명확한 할당을 결정하려면 현지 분석 만 필요합니다 . 분석법은 분석법에서 백만 줄 미만의 코드보다 짧기 때문에 전체 분석법을 분석하는 것이 매우 빠릅니다.
이제 필드는 어떻습니까? 물론 생성자에서 필드를 초기화 할 수 있습니다. 또는 필드 이니셜 라이저. 또는 생성자가 필드를 초기화하는 인스턴스 메소드를 호출 할 수 있습니다. 또는 생성자가 필드를 구체화하지 않는 가상 메서드를 호출 할 수 있습니다. 또는 생성자가 필드를 초기화하는 다른 클래스 ( 라이브러리에 있을 수 있음) 에서 메소드 를 호출 할 수 있습니다 . 정적 필드는 정적 생성자에서 초기화 될 수 있습니다. 정적 필드는 다른 정적 생성자에 의해 초기화 될 수 있습니다 .
기본적으로 필드의 이니셜 라이저는 아직 작성되지 않은 라이브러리에서 선언 될 가상 메소드 내부를 포함 하여 전체 프로그램의 어느 곳에 나 있을 수 있습니다 .
// Library written by BarCorp
public abstract class Bar
{
// Derived class is responsible for initializing x.
protected int x;
protected abstract void InitializeX();
public void M()
{
InitializeX();
Console.WriteLine(x);
}
}
이 라이브러리를 컴파일하는 것은 오류입니까? 그렇다면 BarCorp는 어떻게 버그를 해결해야합니까? x에 기본값을 할당하여? 그러나 이것이 컴파일러가 이미하는 일입니다.
이 라이브러리가 합법적이라고 가정하십시오. FooCorp가 쓰는 경우
public class Foo : Bar
{
protected override void InitializeX() { }
}
입니다 그 오류? 컴파일러는 어떻게 알아낼까요? 유일한 방법은 런타임시 가상 메소드의 선택을 포함하는 경로를 포함 하여 프로그램을 통해 가능한 모든 경로 에서 모든 필드 의 초기화 정적을 추적 하는 전체 프로그램 분석 을 수행하는 것 입니다. 이 문제는 임의로 어려울 수 있습니다 . 수백만 개의 제어 경로를 시뮬레이션하여 실행할 수 있습니다. 로컬 제어 흐름을 분석하는 데 마이크로 초가 걸리며 분석법의 크기에 따라 다릅니다. 전역 제어 흐름을 분석하는 데는 프로그램 의 모든 방법과 모든 라이브러리 의 복잡성에 따라 달라지기 때문에 몇 시간이 걸릴 수 있습니다 .
So why not do a cheaper analysis that doesn't have to analyze the whole program, and just overestimates even more severely? Well, propose an algorithm that works that doesn't make it too hard to write a correct program that actually compiles, and the design team can consider it. I don't know of any such algorithm.
Now, the commenter suggests "require that a constructor initialize all fields". That's not a bad idea. In fact, it is such a not-bad idea that C# already has that feature for structs. A struct constructor is required to definitely-assign all fields by the time the ctor returns normally; the default constructor initializes all the fields to their default values.
What about classes? Well, how do you know that a constructor has initialized a field? The ctor could call a virtual method to initialize the fields, and now we are back in the same position we were in before. Structs don't have derived classes; classes might. Is a library containing an abstract class required to contain a constructor that initializes all its fields? How does the abstract class know what values the fields should be initialized to?
John suggests simply prohibiting calling methods in a ctor before the fields are initialized. So, summing up, our options are:
- Make common, safe, frequently used programming idioms illegal.
- Do an expensive whole-program analysis that makes the compilation take hours in order to look for bugs that probably aren't there.
- Rely upon automatic initialization to default values.
The design team chose the third option.
When I create the same bool within my method, bool check(instead of within the class), i get an error "use of unassigned local variable check". Why?
Because the compiler is trying to prevent you from making a mistake.
Does initializing your variable to false
change anything in this particular path of execution? Probably not, considering default(bool)
is false anyway, but it is forcing you to be aware that this is happening. The .NET environment prevents you from accessing "garbage memory", since it will initialize any value to their default. But still, imagine this was a reference type, and you'd pass an uninitialized (null) value to a method expecting a non-null, and get a NRE at runtime. The compiler is simply trying to prevent that, accepting the fact that this may sometimes result in bool b = false
statements.
Eric Lippert talks about this in a blog post:
The reason why we want to make this illegal is not, as many people believe, because the local variable is going to be initialized to garbage and we want to protect you from garbage. We do in fact automatically initialize locals to their default values. (Though the C and C++ programming languages do not, and will cheerfully allow you to read garbage from an uninitialized local.) Rather, it is because the existence of such a code path is probably a bug, and we want to throw you in the pit of quality; you should have to work hard to write that bug.
Why doesn't this apply to a class field? Well, I assume the line had to be drawn somewhere, and local variables initialization are a lot easier to diagnose and get right, as opposed to class fields. The compiler could do this, but think of all the possible checks it would need to be making (where some of them are independent of the class code itself) in order to evaluate if each field in a class is initialized. I am no compiler designer, but I am sure it would be definitely harder as there are plenty of cases that are taken into account, and has to be done in a timely fashion as well. For every feature you have to design, write, test and deploy and the value of implementing this as opposed to the effort put in would be non-worthy and complicated.
Why do local variables require initialization, but fields do not?
The short answer is that code accessing uninitialised local variables can be detected by the compiler in a reliable way, using static analysis. Whereas this isn't the case of fields. So the compiler enforces the first case, but not the second.
Why do local variables require initialization?
This is no more than a design decision of the C# language, as explained by Eric Lippert. The CLR and the .NET environment do not require it. VB.NET, for example, will compile just fine with uninitialised local variables, and in reality the CLR initialises all uninitialised variables to default values.
The same could occur with C#, but the language designers chose not to. The reason is that initialised variables are a huge source of bugs and so, by mandating initialisation, the compiler helps to cut down on accidental mistakes.
Why don't fields require initialization?
So why doesn't this compulsory explicit initialisation happen with fields within a class? Simply because that explicit initialisation could occur during construction, through a property being called by an object initializer, or even by a method being called long after the event. The compiler cannot use static analysis to determine if every possible path through the code leads to the variable being explicitly initialised before us. Getting it wrong would be annoying, as the developer could be left with valid code that won't compile. So C# doesn't enforce it at all and the CLR is left to automatically initialise fields to a default value if not explicitly set.
What about collection types?
C#'s enforcement of local variable initialisation is limited, which often catches developers out. Consider the following four lines of code:
string str;
var len1 = str.Length;
var array = new string[10];
var len2 = array[0].Length;
The second line of code won't compile, as it's trying to read an uninitialised string variable. The fourth line of code compiles just fine though, as array
has been initialised, but only with default values. Since the default value of a string is null, we get an exception at run-time. Anyone who's spent time here on Stack Overflow will know that this explicit/implicit initialisation inconsistency leads to a great many "Why am I getting a “Object reference not set to an instance of an object” error?" questions.
Good answers above, but I thought I'd post a much simpler/shorter answer for people to lazy to read a long one (like myself).
Class
class Foo {
private string Boo;
public Foo() { /** bla bla bla **/ }
public string DoSomething() { return Boo; }
}
Property Boo
may or may not have been initialized in the constructor. So when it finds return Boo;
it doesn't assume that it's been initialized. It simply suppresses the error.
Function
public string Foo() {
string Boo;
return Boo; // triggers error
}
The { }
characters define the scope of a block of code. The compiler walks the branches of these { }
blocks keeping track of stuff. It can easily tell that Boo
was not initialized. The error is then triggered.
Why does the error exist?
The error was introduced to reduce the number of lines of code required to make source code safe. Without the error the above would look like this.
public string Foo() {
string Boo;
/* bla bla bla */
if(Boo == null) {
return "";
}
return Boo;
}
From the manual:
The C# compiler does not allow the use of uninitialized variables. If the compiler detects the use of a variable that might not have been initialized, it generates compiler error CS0165. For more information, see Fields (C# Programming Guide). Note that this error is generated when the compiler encounters a construct that might result in the use of an unassigned variable, even if your particular code does not. This avoids the necessity of overly-complex rules for definite assignment.
Reference: https://msdn.microsoft.com/en-us/library/4y7h161d.aspx
'programing tip' 카테고리의 다른 글
Python 3.3 이상에서 패키지에 __init__.py가 필요하지 않습니까? (0) | 2020.06.22 |
---|---|
기능 A가 기능 B에만 필요한 경우 A를 B 내부에 정의해야합니까? (0) | 2020.06.22 |
엔티티 본문없이 HTTP POST를 수행하는 것은 나쁜 습관으로 간주됩니까? (0) | 2020.06.21 |
IE9의 자리 표시 자 (0) | 2020.06.21 |
목록보기가 데이터로로드되기 전에 목록보기가있는 활동에서 진행률 표시 줄 (원)을 표시하는 방법 (0) | 2020.06.21 |