C # 및 Java (및 다른 언어도 가능)에서 “try”블록에 선언 된 변수는 해당 “catch”또는 “finally”블록의 범위에 속하지 않습니다. 예를 들어 다음 코드는 컴파일되지 않습니다.
try {
String s = "test";
// (more code...)
}
catch {
Console.Out.WriteLine(s); //Java fans: think "System.out.println" here instead
}
이 코드에서는 catch 블록의 s에 대한 참조에서 컴파일 타임 오류가 발생합니다. s는 try 블록의 범위에만 있기 때문입니다. Java에서 컴파일 오류는 “s 확인할 수 없습니다”이며 C #에서는 “현재 컨텍스트에 ‘s’이름이 없습니다”입니다.
이 문제에 대한 일반적인 해결책은 try 블록 대신 try 블록 바로 앞에 변수를 선언하는 것 같습니다.
String s;
try {
s = "test";
// (more code...)
}
catch {
Console.Out.WriteLine(s); //Java fans: think "System.out.println" here instead
}
그러나 적어도 나에게, (1) 이것은 어리석은 해결책처럼 느껴지고 (2) 변수가 프로그래머가 의도 한 것보다 더 넓은 범위를 갖습니다 (메소드의 맥락 에서만가 아니라 메소드의 전체 나머지). 마지막으로 시도하십시오.
내 질문은이 언어 디자인 결정의 근거가 무엇입니까 (Java, C # 및 / 또는 기타 적용 가능한 언어)?
답변
두가지:
-
일반적으로 Java는 전역 및 기능의 두 가지 범위 만 있습니다. 그러나 try / catch는 예외입니다. 예외가 발생하고 예외 개체에 변수가 할당되면 해당 개체 변수는 “캐치”섹션 내에서만 사용할 수 있으며 캐치가 완료 되 자마자 소멸됩니다.
-
(그리고 더 중요하게). try 블록에서 예외가 발생한 위치를 알 수 없습니다. 변수가 선언되기 전에있을 수 있습니다. 따라서 catch / finally 절에 사용할 수있는 변수를 말하는 것은 불가능합니다. 제안한대로 범위를 지정하는 다음 경우를 고려하십시오.
try { throw new ArgumentException("some operation that throws an exception"); string s = "blah"; } catch (e as ArgumentException) { Console.Out.WriteLine(s); }
이것은 분명히 문제입니다. 예외 처리기에 도달하면 s가 선언되지 않습니다. 어획량은 예외적 인 상황을 처리하고 최종적으로 실행 되어야 함을 감안할 때 안전하고 컴파일 타임에 문제를 선언하는 것이 런타임보다 훨씬 낫습니다.
답변
캐치 블록의 선언 부분에 도달했다는 것을 어떻게 확신 할 수 있습니까? 인스턴스화에서 예외가 발생하면 어떻게됩니까?
답변
전통적으로 C 스타일 언어에서 중괄호 안에서 발생하는 일은 중괄호 안에 유지됩니다. 그런 범위에서 변수의 수명을 연장하는 것은 대부분의 프로그래머에게는 직관적이지 않을 것이라고 생각합니다. try / catch / finally 블록을 다른 버팀대 안에 넣어서 원하는 것을 얻을 수 있습니다. 예 :
... code ...
{
string s = "test";
try
{
// more code
}
catch(...)
{
Console.Out.WriteLine(s);
}
}
편집 : 나는 모든 규칙 에 예외 가 있다고 생각합니다 . 유효한 C ++는 다음과 같습니다.
int f() { return 0; }
void main()
{
int y = 0;
if (int x = f())
{
cout << x;
}
else
{
cout << x;
}
}
x의 범위는 조건부, then 절 및 else 절입니다.
답변
다른 모든 사람들은 기본 사항을 가져 왔습니다. 블록에서 발생하는 일이 블록에 남아 있습니다. 그러나 .NET의 경우 컴파일러의 생각을 조사하는 것이 도움이 될 수 있습니다. 예를 들어 다음 try / catch 코드를 사용하십시오 (StreamReader가 블록 외부에 올바르게 선언되어 있음에 유의하십시오).
static void TryCatchFinally()
{
StreamReader sr = null;
try
{
sr = new StreamReader(path);
Console.WriteLine(sr.ReadToEnd());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
if (sr != null)
{
sr.Close();
}
}
}
이것은 MSIL에서 다음과 유사한 것으로 컴파일됩니다.
.method private hidebysig static void TryCatchFinallyDispose() cil managed
{
// Code size 53 (0x35)
.maxstack 2
.locals init ([0] class [mscorlib]System.IO.StreamReader sr,
[1] class [mscorlib]System.Exception ex)
IL_0000: ldnull
IL_0001: stloc.0
.try
{
.try
{
IL_0002: ldsfld string UsingTest.Class1::path
IL_0007: newobj instance void [mscorlib]System.IO.StreamReader::.ctor(string)
IL_000c: stloc.0
IL_000d: ldloc.0
IL_000e: callvirt instance string [mscorlib]System.IO.TextReader::ReadToEnd()
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: leave.s IL_0028
} // end .try
catch [mscorlib]System.Exception
{
IL_001a: stloc.1
IL_001b: ldloc.1
IL_001c: callvirt instance string [mscorlib]System.Exception::ToString()
IL_0021: call void [mscorlib]System.Console::WriteLine(string)
IL_0026: leave.s IL_0028
} // end handler
IL_0028: leave.s IL_0034
} // end .try
finally
{
IL_002a: ldloc.0
IL_002b: brfalse.s IL_0033
IL_002d: ldloc.0
IL_002e: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0033: endfinally
} // end handler
IL_0034: ret
} // end of method Class1::TryCatchFinallyDispose
우리는 무엇을 볼 수 있습니까? MSIL은 블록을 존중합니다. C #을 컴파일 할 때 생성 된 기본 코드의 일부입니다. 범위는 C # 사양에 딱 맞게 설정되어있는 것이 아니라 CLR 및 CLS 사양에도 적용됩니다.
범위는 당신을 보호하지만 때로는 해결해야합니다. 시간이 지남에 따라 익숙해 져 자연스럽게 느껴지기 시작합니다. 다른 사람들이 말했듯이 블록에서 발생하는 일이 해당 블록에 유지됩니다. 무언가를 공유하고 싶습니까? 당신은 블록 밖으로 나가야합니다 …
답변
C ++에서 자동 변수의 범위는이를 둘러싸는 중괄호로 제한됩니다. 왜 중괄호 밖에서 try 키워드를 내려서 이것이 다른 것이기를 기대할까요?
답변
ravenspoint가 지적한 것처럼, 모든 사람들은 변수가 정의 된 블록에 국한 될 것으로 기대합니다. try
블록을 소개합니다 .catch
.
try
및 에 모두 지역 변수가 필요한 경우 catch
두 블록을 모두 블록으로 묶어보십시오.
// here is some code
{
string s;
try
{
throw new Exception(":(")
}
catch (Exception e)
{
Debug.WriteLine(s);
}
}
답변
간단한 대답은 C와 구문을 상속받은 대부분의 언어는 블록 범위입니다. 즉, 변수가 하나의 블록, 즉 {} 내부에 정의되어 있으면 해당 범위가됩니다.
그런데 예외는 비슷한 구문을 가지고 있지만 기능 범위가있는 JavaScript입니다. JavaScript에서 try 블록에 선언 된 변수는 catch 블록과 그 밖의 다른 곳에서 포함 된 함수의 범위에 있습니다.