VB.NET 및 C #의 값에 대해 null을 확인하는 데 차이가있는 이유는 무엇입니까?


110

VB.NET 에서는 다음이 발생합니다.

Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing

y = 5
If x <> y Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false") '' <-- I got this. Why?
End If

그러나 C #에서는 다음이 발생합니다.

decimal? x = default(decimal?);
decimal? y = default(decimal?);

y = 5;
if (x != y)
{
    Debug.WriteLine("true"); // <-- I got this -- I'm with you, C# :)
}
else
{
    Debug.WriteLine("false");
}

왜 차이가 있습니까?


22
무섭 네요.
Mikeb 2013 년

8
나는 default(decimal?)0이 아니라 null.
Ryan Frame

7
@RyanFrame 아니. 이 때문에 nullable 형식을 , 그것은 반환null
네르 Gönül

4
아 그래 ... 바로 ... 비주얼 베이직에서 If조건문 부울로 평가하는 데 필요하지 않습니다 ... uuuugh 편집 : 그래서는 Nothing <> Anything = Nothing(가)에서 어떤 결과 If부정적인 / 다른 경로를 복용.
Chris Sinclair 2013 년

13
@JMK : Null, Nothing 및 Empty는 실제로 모두 미묘하게 다릅니다. 그것들이 모두 같으면 세 개가 필요하지 않을 것입니다.
Eric Lippert 2013 년

답변:


88

VB.NET과 C # .NET은 사용에 대해 서로 다른 가정을 한 서로 다른 팀에 의해 구축 된 서로 다른 언어입니다. 이 경우 NULL 비교의 의미입니다.

개인적으로 선호하는 것은 VB.NET 의미 체계에 대한 것인데, 본질적으로 NULL에 "아직 알지 못함"이라는 의미를 부여합니다. 그런 다음 5를 "아직 모르겠다"와 비교합니다. 당연히 "나는 아직 모른다"입니다. 즉 NULL입니다. 이것은 SQL 데이터베이스에서 NULL의 동작을 미러링하는 추가적인 이점이 있습니다. 이것은 또한 여기에 설명 된대로 3 값 논리에 대한보다 표준적인 (C #보다) 해석입니다 .

C # 팀은 NULL이 무엇을 의미하는지에 대해 다른 가정을했고 결과적으로 사용자가 보여주는 동작 차이가 발생했습니다. Eric Lippert는 C #에서 NULL의 의미에 대한 블로그를 작성했습니다 . Eric Lippert : "저는 VB / VBScript 및 JScript의 null 의미에 대해서도 여기여기에 썼습니다 ."

NULL 값이 가능한 모든 환경에서 제외 된 중간의 법칙 (즉, A 또는 ~ A가 tautologically 참)을 더 이상 신뢰할 수 없다는 것을 인식하는 것이 중요합니다.

최신 정보:

A bool(a와 반대 bool?)는 TRUE 및 FALSE 값만 사용할 수 있습니다. 그러나 NULL의 언어 구현은 NULL이 표현식을 통해 전파되는 방식을 결정해야합니다. VB에서 표현식 5=null5<>null둘 다 거짓을 반환합니다. C 상기 비교 식 #에서, 5==null그리고 5!=null단지 - [PG 업데이트 2014년 3월 2일] 이 거짓을 반환. 그러나 null을 지원하는 모든 환경에서 프로그래머는 해당 언어가 사용하는 진리표와 null 전파를 알아야합니다.

최신 정보

의미론에 대한 Eric Lippert의 블로그 기사 (아래 주석에서 언급 됨)는 현재 다음 위치에 있습니다.


4
링크 주셔서 감사합니다. 또한 VB / VBScript 및 JScript의 null 의미에 대해 blogs.msdn.com/b/ericlippert/archive/2003/09/30/53120.aspx 및 여기 : blogs.msdn.com/b/ericlippert/에
Eric Lippert

27
그리고 이런 식으로 C #을 VB와 호환되지 않게하기로 한 결정은 논란의 여지가 있습니다. 나는 당시 언어 디자인 팀에 없었지만이 결정에 들어간 논쟁의 양은 상당했습니다.
Eric Lippert 2013 년

2
@ BlueRaja-DannyPflughoeft C #에서는 bool3 개의 값을 가질 수없고 2 개만 가질 수 있습니다. 그것은 bool?세 가지 값을 가질 수 있습니다. operator ==그리고 operator !=둘 다 피연산자의 유형에 관계없이 bool, not을 반환 bool?합니다. 또한, if문 만 받아 들일 수 bool하는 없습니다 bool?.
Servy

1
C #으로 표현 5=null하고 5<>null유효하지 않습니다. 그리고의 5 == null5 != null, 반환하는 두 번째가 확실 false합니까?
Ben Voigt 2014 년

1
@BenVoigt : 감사합니다. 그 모든 찬성 투표와 당신은 그 오타를 가장 먼저 발견합니다. ;-)
Pieter Geerkens 2014 년

37

때문에 x <> y반환 Nothing하는 대신 true. 정의되지 않았기 때문에 단순히 정의 x되지 않았습니다. (SQL null과 유사).

참고 : VB.NET Nothing<> C # null.

또한 값이있는 Nullable(Of Decimal)경우에만 값을 비교해야 합니다.

따라서 위의 VB.NET은 다음과 유사하게 비교됩니다 (덜 부정확 해 보입니다).

If x.HasValue AndAlso y.HasValue AndAlso x <> y Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false")  
End If

VB.NET 언어 사양 :

7.1.1가능 값 유형 ... 널 가능 값 유형은 널값뿐만 아니라 널 불가능한 유형 버전과 동일한 값을 포함 할 수 있습니다. 따라서 nullable 값 형식의 경우 해당 형식의 변수에 Nothing을 할당하면 변수 값이 값 형식의 0 값이 아닌 null 값으로 설정됩니다.

예를 들면 :

Dim x As Integer = Nothing
Dim y As Integer? = Nothing

Console.WriteLine(x) ' Prints zero '
Console.WriteLine(y) ' Prints nothing (because the value of y is the null value) '

16
"VB.NET Nothing <> C # null"은 C #의 경우 true를 반환하고 VB.Net의 경우 false를 반환합니까? 농담입니다
:-p

17

생성 된 CIL을보십시오 (둘 다 C #으로 변환했습니다).

씨#:

private static void Main(string[] args)
{
    decimal? x = null;
    decimal? y = null;
    y = 5M;
    decimal? CS$0$0000 = x;
    decimal? CS$0$0001 = y;
    if ((CS$0$0000.GetValueOrDefault() != CS$0$0001.GetValueOrDefault()) ||
        (CS$0$0000.HasValue != CS$0$0001.HasValue))
    {
        Console.WriteLine("true");
    }
    else
    {
        Console.WriteLine("false");
    }
}

비주얼 베이직 :

[STAThread]
public static void Main()
{
    decimal? x = null;
    decimal? y = null;
    y = 5M;
    bool? VB$LW$t_struct$S3 = new bool?(decimal.Compare(x.GetValueOrDefault(), y.GetValueOrDefault()) != 0);
    bool? VB$LW$t_struct$S1 = (x.HasValue & y.HasValue) ? VB$LW$t_struct$S3 : null;
    if (VB$LW$t_struct$S1.GetValueOrDefault())
    {
        Console.WriteLine("true");
    }
    else
    {
        Console.WriteLine("false");
    }
}

Visual Basic의 비교에서 Nullable <bool> (bool, false 또는 true가 아님)을 반환하는 것을 볼 수 있습니다. 그리고 bool로 변환 된 undefined는 false입니다.

NothingNothingVisual Basic에서는 false가 아닌 always 인 것과 비교합니다 (SQL에서와 동일).


왜 시행 착오로 질문에 대답합니까? 언어 사양에서 가능해야합니다.
David Heffernan 2013 년

3
@DavidHeffernan, 이것은 매우 모호하지 않은 언어의 차이를 보여주기 때문입니다.
nothrow 2013 년

2
@Yossarian 언어 사양이 문제에 대해 모호하다고 생각합니다. 동의하지 않습니다. IL은 변경 될 수있는 구현 세부 사항입니다. 사양이 아닙니다.
Servy

2
@DavidHeffernan : 나는 당신의 태도가 마음에 들며 시도해 보도록 권장합니다. VB 언어 사양은 때때로 구문 분석하기 어려울 수 있습니다. Lucian은 몇 년 동안 그것을 개선해 왔지만 이러한 종류의 코너 케이스의 정확한 의미를 파악하기는 여전히 상당히 어려울 수 있습니다. 사양 사본을 구하고 조사를 수행하고 발견 한 내용을보고하는 것이 좋습니다.
Eric Lippert 2013 년

2
당신이 제공 한 IL 코드를 실행 한 결과 @Yossarian 변경 될 수 없지만이 보여 IL 코드로 컴파일됩니다 제공하는 C #을 / VB 코드가 있다는 것입니다 IL인지의 행동만큼 (변경 될 수를 또한 언어 사양의 정의와 일치).
Servy 2013 년

6

여기에서 관찰되는 문제는보다 일반적인 문제의 특별한 경우입니다. 이는 적어도 일부 상황에서 유용 할 수있는 서로 다른 평등 정의의 수가 일반적으로 사용되는 표현 수단의 수를 초과한다는 것입니다. 이 문제는 평등을 테스트하는 다른 수단이 다른 결과를 산출하는 것이 혼란 스럽다는 불행한 믿음으로 인해 악화되는 경우가 있으며, 가능할 때마다 동일한 결과를 산출하는 다른 형태의 평등을 사용함으로써 그러한 혼란을 피할 수 있습니다.

실제로, 혼란의 근본적인 원인은 서로 다른 의미가 서로 다른 상황에서 유용하다는 사실에도 불구하고 서로 다른 형태의 평등 및 불평등 테스트가 동일한 결과를 산출 할 것으로 예상되어야한다는 잘못된 믿음입니다. 예를 들어, 산술적 관점에서 Decimal후행 0의 수만 다른 것으로 비교할 수있는 것이 유용합니다 . double양수 0 및 음수 0과 같은 값도 마찬가지입니다 . 반면 캐싱 또는 인턴 관점에서 이러한 의미는 치명적일 수 있습니다. 예를 들면, 일이 있었다 가정 Dictionary<Decimal, String>되도록 myDict[someDecimal]같아야를 someDecimal.ToString(). 그러한 대상은 하나가 많은 경우 합리적으로 보일 것입니다Decimal문자열로 변환하기를 원했고 많은 중복이있을 것으로 예상 한 값. 안타깝게도 이러한 캐싱을 사용하여 12.3m 및 12.40m를 변환 한 다음 12.30m 및 12.4m를 변환하면 후자의 값은 "12.30"및 "12.4"대신 "12.3"및 "12.40"을 생성합니다.

당면한 문제로 돌아가서, nullable 객체가 같은지 비교하는 현명한 방법이 하나 이상 있습니다. C #은 ==연산자가 Equals. VB.NET 은 그 동작을 원하는 사람은 누구나 .NET Framework를 Equals사용할 수 있으므로 다른 언어의 동작을 반영해야한다는 관점을 취합니다 Equals. 어떤 의미에서 올바른 해결책은 3 방향 "if"구조를 갖는 것이며, 조건식이 3 값 결과를 반환하는 경우 코드에서 null케이스 에서 발생해야하는 작업을 지정해야합니다 . 그것은 언어의 옵션이 아니기 때문에 차선책은 단순히 다른 언어가 작동하는 방식을 배우고 동일하지 않다는 것을 인식하는 것입니다.

덧붙여서, C에없는 Visual Basic의 "Is"연산자는 nullable 개체가 실제로 null인지 여부를 테스트하는 데 사용할 수 있습니다. if테스트가를 허용 해야하는지에 대해 합리적으로 질문 할 수 있지만 Boolean?, nullable 유형에서 호출 될 때 Boolean?대신 일반 비교 연산자가 반환되도록 하는 Boolean것은 유용한 기능입니다. 덧붙여서, VB.NET에서 대신 같음 연산자를 사용하려고 시도 Is하면 비교 결과가 항상이라는 경고가 표시되고 무언가가 null인지 테스트 Nothing하려면 사용해야 Is합니다.


C #에서 클래스가 null인지 여부 테스트는 == null. 그리고 nullable 값 형식에 값이 있는지 여부를 테스트하는 작업은 .hasValue. Is Nothing운영자 는 어떤 용도로 사용 됩니까? C #에는 is있지만 형식 호환성을 테스트합니다. 이것에 비추어 볼 때, 나는 당신의 마지막 단락이 무엇을 말하려고하는지 정말로 잘 모르겠습니다.
ErikE

@ErikE : vb.net과 C #은 둘 다 비교를 사용하여 값에 대해 nullable 형식을 확인할 수 있도록 허용 null하지만, 두 언어 모두 HasValue적어도 형식이 알려진 경우 에이를 확인을 위한 구문 설탕으로 취급하지만 (확실하지 않습니다) 제네릭에 대해 생성되는 코드).
supercat

제네릭에서는 nullable 형식 및 오버로드 해결과 관련된 까다로운 문제가 발생할 수 있습니다.
ErikE

3

게시물 도움 될 수 있습니다.

내가 정확하게 기억한다면 VB의 'Nothing'은 "기본값"을 의미합니다. 값 형식의 경우 기본값이고 참조 형식의 경우 null입니다. 따라서 구조체에 아무것도 할당하지 않아도 전혀 문제가되지 않습니다.


3
이것은 질문에 대한 답이 아닙니다.
David Heffernan

아니요, 아무것도 명확하지 않습니다. 문제는 <>VB 의 연산자와 nullable 형식에서 작동하는 방법에 관한 것입니다.
David Heffernan

2

이것은 VB의 확실한 기이함입니다.

VB에서 두 개의 nullable 형식을 비교하려면 Nullable.Equals().

귀하의 예에서는 다음과 같아야합니다.

Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing

y = 5
If Not Nullable.Equals(x, y) Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false")
End If

5
익숙하지 않은 "이상 함"입니다. Pieter Geerkens의 답변을 참조하십시오.
rskar

VB가의 동작을 재현하지 않는 것이 이상하다고 생각합니다 Nullable<>.Equals(). 같은 방식으로 작동 할 것으로 예상 할 수 있습니다 (C #이 수행하는 방식).
Matthew Watson

"사람이 기대할 수있는 것"에서와 같이 기대는 사람이 경험 한 것에 대한 것입니다. C #은 Java 사용자의 기대치를 염두에두고 설계되었습니다. Java는 C / C ++ 사용자의 기대치를 염두에두고 설계되었습니다. 좋든 나쁘 든 VB.NET은 VB6 사용자의 기대치를 염두에두고 설계되었습니다. stackoverflow.com/questions/14837209/…stackoverflow.com/questions/10176737/…
rskar

1
@MatthewWatson의 정의는 Nullable.NET의 첫 번째 버전에는 존재하지 않았으며 C # 및 VB.NET이 얼마 동안 사용되지 않았고 이미 null 전파 동작을 결정한 후에 생성되었습니다. 솔직히 언어가 몇 년 동안 만들어지지 않은 유형과 일치 할 것이라고 기대하십니까? VB.NET 프로그래머의 관점에서 볼 때 언어와 일치하지 않는 Nullable.Equals입니다. (C # 및 VB가 모두 동일한 사용하는 것이 감안할 때 Nullable정의를,이 두 언어와 일치 할 수있는 방법이 없었습니다.)
Servy

0

VB 코드가 잘못되었습니다. "x <> y"를 "x = y"로 변경하면 결과로 "false"가 표시됩니다. nullable 인스턴스에 대한 가장 일반적인 표현 방법은 "Not x.Equals (y)"이며 C #의 "x! = y"와 동일한 동작을 생성합니다.


1
하지 않는 x것입니다 nothing,이 경우 x.Equals(y)예외가 발생합니다.
Servy 2011 년

@Servy : (수년 후) 이것을 다시 우연히 발견했고 내가 당신을 수정하지 않았다는 것을 알아 챘습니다. "x.Equals (y)"는 nullable 유형 인스턴스 'x'에 대한 예외를 던지지 않습니다 . Nullable 형식은 컴파일러에서 다르게 처리됩니다.
Dave Doknjas

특히 'null'로 초기화 된 nullable 인스턴스는 실제로 null로 설정된 변수가 아니라 값이 설정되지 않은 System.Nullable 인스턴스입니다.
Dave Doknjas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.