C #에서 부호없는 int를 사용하지 않아야합니까?


23

최근에 C #에서 부호없는 정수 사용에 대해 생각했습니다 (그리고 다른 "고급 언어"에 대해서는 비슷한 주장이 있다고 생각합니다)

정수가 필요한 경우 일반적으로 정수 크기의 딜레마에 직면하지 않는 경우 Person 클래스의 age 속성을 예로들 수 있습니다 (그러나 질문은 properties로 제한되지 않습니다). 그것을 염두에두고, 내가 알 수있는 한 부호없는 정수 ( "int")에 비해 부호없는 정수 ( "uint")를 사용하는 것의 한 가지 장점-가독성이 있습니다. 나이가 긍정적 일 수 있다는 생각을 표현하고 싶다면 나이 유형을 uint로 설정하여이를 달성 할 수 있습니다.

반면 부호없는 정수에 대한 계산은 모든 종류의 오류를 초래할 수 있으며 2 세를 빼는 등의 작업을 수행하기가 어렵습니다. (이것은 Java가 부호없는 정수를 생략 한 이유 중 하나입니다.)

C #의 경우 세터의 가드 절이 두 가지 이점을 모두 제공하는 솔루션이라고 생각할 수 있지만 예를 들어 나이가 일부 방법으로 전달되는 경우에는 적용 할 수 없습니다. 해결 방법은 Age라는 클래스를 정의하고 속성 나이를 유일하게 유지하는 것이지만,이 패턴으로 인해 많은 클래스를 만들게되며 혼란의 원인이 될 수 있습니다 (다른 개발자는 객체가 래퍼 일 때 알 수 없음) 그리고 그것이 더 독창적 일 때).

이 문제와 관련하여 일반적인 모범 사례는 무엇입니까? 이 시나리오를 어떻게 처리해야합니까?



1
또한 서명되지 않은 int는 CLS 규격이 아니므로 다른 .NET 언어에서 API를 사용하는 API를 호출 할 수 없습니다.
Nathan Cooper

2
@NathanCooper : ... "에서 사용할 API를 호출 할 수 없습니다 몇 가지 다른 언어를". 메타 데이터는 표준화되어 있으므로 서명되지 않은 형식을 지원하는 모든 .NET 언어는 정상적으로 상호 운용됩니다.
Ben Voigt

5
귀하의 특정 예를 설명하기 위해 우선 나이라는 속성이 없습니다. Birthday 또는 CreationTime 등의 속성이 있고 나이를 계산합니다.
Eric Lippert

2
"...하지만이 패턴을 사용하면 많은 클래스를 만들 수 있고 혼란의 원인이 될 것입니다." 실제로 이것이 올바른 일입니다. 악명 높은 Primitive Obsession 안티 패턴을 검색하십시오 .
Songo

답변:


23

.NET Framework의 설계자는 몇 가지 이유로 "일반 목적 번호"로 32 비트 부호있는 정수를 선택했습니다.

  1. 음수, 특히 -1을 처리 할 수 ​​있습니다 (프레임 워크에서 오류 조건을 나타 내기 위해 사용합니다. 따라서 인덱싱 컨텍스트에서 음수가 아닌 경우에도 인덱싱이 필요한 모든 곳에서 부호있는 int가 사용됩니다).
  2. 거의 모든 곳에서 경제적으로 사용될 수있을 정도로 작 으면서도 대부분의 목적을 수행 할 수있을만큼 큽니다.

부호없는 정수를 사용하는 이유는 가독성 이 아니기 때문입니다 . 부호없는 int 만 제공하는 수학을 얻는 기능이 있습니다.

가드 조항, 검증 및 계약 전제 조건은 유효한 숫자 범위를 보장하기 위해 완벽하게 수용 가능한 방법입니다. 실제 숫자 범위가 0에서 2 32 -1 사이의 숫자 (또는 사용자가 선택한 숫자 유형의 숫자)에 정확히 일치하는 경우는 거의 없으므로 a uint를 사용 하여 인터페이스 계약을 양수로 제한 하는 것은 요점 옆에.


2
좋은 대답입니다! 또한 부호없는 int가 실제로 실수로 더 많은 오류를 생성하는 경우가 있습니다 (아마도 즉시 발견되었지만 약간 혼란 스럽지만)-어떤 크기는 정수이므로 부호없는 int 카운터와 반대로 반복하는 것을 상상하십시오. for (uint j=some_size-1; j >= 0; --j)-whoops ( 이것이 C #의 문제인지 확실하지 않습니다!) 코드 에서이 문제를 발견하여 가능한 한 C 측에서 unsigned int를 사용하려고 시도했습니다. 그리고 int나중에 선호하도록 변경하고 결국 컴파일러 경고가 줄어들어 인생이 훨씬 쉬워졌습니다.

14
"실제 숫자 범위는 0에서 2 ^ 32-1 사이의 숫자에 해당하지 않습니다." 내 경험상 2 ^ 31보다 큰 숫자가 필요할 경우 2 ^ 32보다 큰 숫자가 필요할 가능성이 매우 높으므로 int64 at (64)로 이동할 수도 있습니다. 그 시점.
메이슨 휠러

3
@ Panzercrisis : 조금 심각합니다. 아마 말을 더 정확한 것 "을 사용하여 int대부분의 시간 즉 기존의 규칙이기 때문에, 그것은 대부분의 사람들이 일상적으로 사용하는 것으로 예상가는거야. 사용 uint당신이의 특별한 capabilites을 필요로 할 때 uint." 프레임 워크 디자이너는이 규칙을 광범위하게 따르기로 결정했기 때문에 uint많은 프레임 워크 컨텍스트에서 사용할 수 없습니다 (유형이 호환되지 않음).
Robert Harvey

2
@Panzercrisis 지나치게 강한 문구 일 수 있습니다. 그러나 win32 api를 호출 할 때를 제외하고 C #에서 부호없는 유형을 사용한 적이 있는지 확실하지 않습니다 (일반적으로 상수 / 플래그 등은 부호가 없습니다).
Dan Neely

4
실제로는 매우 드 rare니다. 부호없는 정수를 사용하는 유일한 시간은 비트 트위들 링 시나리오입니다.
Robert Harvey

8

일반적으로 가능한 한 가장 구체적인 데이터 형식을 사용해야합니다.

예를 들어, Entity Framework를 사용하여 데이터베이스에서 데이터를 가져 오는 경우 EF는 데이터베이스에서 사용 된 것과 가장 가까운 데이터 유형을 자동으로 사용합니다.

C #에는 두 가지 문제가 있습니다.
첫째, 대부분의 C # 개발자는을 사용해야 int합니다 (이유가없는 한 long). 이는 다른 개발자가 데이터 유형을 확인하지 않기 때문에 위에서 언급 한 오버플로 오류가 발생 함을 의미합니다. 두 번째, 그리고 더 중요한 문제이며, /이었다 .NET의 것을 원래의 산술 연산자가 지원되는 int, uint, long, ulong, float, 더블, 그리고 decimal*. 이것은 오늘날에도 여전히 적용됩니다 ( C # 5.0 언어 사양의 7.8.4 섹션 참조 ). 다음 코드를 사용하여 직접 테스트 할 수 있습니다.

byte a, b;
a = 1;
b = 2;
var c = a - b;      //In visual studio, hover over "var" and the tip will indicate the data type, or you can get the value from cName below.
string cName = c.GetType().Namespace + '.' + c.GetType().Name;

우리의 결과는 byte- byte이다 int( System.Int32)을.

이 두 가지 문제는 "정수에 정수만 사용"연습을 일으켰습니다.

따라서 귀하의 질문에 대답하기 위해 C #에서는 일반적으로 다음 사항을 준수 int하지 않는 것이 좋습니다 .

  • 자동화 된 코드 생성기는 다른 값 (예 : Entity Framework)을 사용했습니다.
  • 프로젝트의 다른 모든 개발자는 일반적이지 않은 데이터 형식을 사용하고 있음을 알고 있습니다 (데이터 형식을 사용했음을 나타내는 주석 포함 및 이유).
  • 덜 일반적인 데이터 형식은 이미 프로젝트에서 일반적으로 사용됩니다.
  • 이 프로그램은 덜 일반적인 데이터 형식의 이점을 필요로 (A의 차이 있도록, 당신은 RAM에 보관해야이 100 만 달러를 가지고 byte하고 intint하고는 long중요하다, 또는 부호의 산술 차이가 이미 언급).

데이터에 대해 수학을 수행해야하는 경우 공통 유형을 고수하십시오.
한 유형에서 다른 유형으로 캐스트 할 수 있습니다. CPU 관점에서는 효율성이 떨어질 수 있으므로 7 가지 일반적인 유형 중 하나를 사용하는 것이 더 나을 수도 있지만 필요한 경우 옵션입니다.

열거 형 ( enum)은 위 가이드 라인에 대한 개인적 예외 중 하나입니다. 몇 가지 옵션 만 있으면 열거 형 을 바이트 또는 단락으로 지정합니다 . 플래그가 지정된 열거 형의 마지막 비트가 필요한 경우 uint16 진수를 사용하여 플래그 값을 설정할 수 있도록 유형을 지정합니다 .

값 제한 코드가있는 속성을 사용하는 경우 요약 태그에 어떤 제한이 있으며 그 이유를 설명해야합니다.

* C # 별칭이므로 .NET 이름 대신 C # 별칭이 사용됩니다 System.Int32.

참고 : .NET 개발자 (찾을 수없는)의 블로그 나 기사가 있었으므로 제한된 수의 산술 함수와 몇 가지 이유에 대해 걱정하지 않았습니다. 내가 기억하는 것처럼 그들은 다른 데이터 유형에 대한 지원을 추가 할 계획이 없다고 지적했다.

참고 : Java는 서명되지 않은 데이터 유형을 지원하지 않으며 이전에는 8 또는 16 비트 정수를 지원하지 않았습니다. 많은 C # 개발자가 Java 배경을 가지고 있거나 두 언어로 작업해야하므로 한 언어의 한계가 다른 언어에 인위적으로 적용되는 경우가 있습니다.


나의 일반적인 경험 법칙은 단순히 "당신이 할 수 없다면 int를 사용하십시오"입니다.
PerryC

@PerryC 이것이 가장 일반적인 관례라고 생각합니다. 내 대답의 요점은 언어 기능을 사용할 수있는보다 완전한 규칙을 제공하는 것이 었습니다.
Trisped

6

주로 나타내는 데이터와 계산의 중간 단계라는 두 가지 사항을 알고 있어야합니다.

unsigned int우리는 일반적으로 부정적인 연령을 고려하지 않기 때문에 확실히 나이를 갖는 것이 합리적 입니다. 그러나 당신은 한 연령을 다른 연령에서 빼는 것을 언급합니다. 우리가 한 정수를 다른 정수에서 맹목적으로 빼면 이전에 음수 연령이 의미가 없다는 데 동의하더라도 음수로 끝날 수 있습니다. 따라서이 경우 부호있는 정수로 계산을 수행하려고합니다.

서명되지 않은 값이 나쁜지 여부와 관련하여 서명되지 않은 값이 잘못되었다는 것은 큰 일반화입니다. Java는 언급 한 바와 같이 부호없는 값을 가지고 있지 않으며 끊임없이 귀찮게합니다. A byte는 0-255 또는 0x00-0xFF의 값을 가질 수 있습니다. 그러나 127 (0x7F)보다 큰 바이트를 인스턴스화하려면 음수로 쓰거나 정수를 바이트로 캐스트해야합니다. 다음과 같은 코드가 생깁니다.

byte a = 0x80; // Won't compile!
byte b = (byte) 0x80;
byte c = -128; // Equal to b

위의 내용은 끝이 없습니다. 바이트를 197로 가질 수는 없지만 바이트를 다루는 대부분의 제정신이있는 사람들에게는 완벽하게 유효한 값이지만. 정수를 캐스팅하거나 음수 값을 찾을 수 있습니다 (이 경우 197 == -59). 또한 이것을 고려하십시오 :

byte a = 70;
byte b = 80;
byte c = a + b; // c == -106

보시다시피, 유효한 값으로 2 바이트를 추가하고 유효한 값으로 바이트로 끝나면 부호가 변경됩니다. 뿐만 아니라 70 + 80 == -106이라는 것이 즉시 명확하지 않습니다. 기술적으로 이것은 오버플로이지만 내 마음에 (인간으로서) 바이트는 0xFF 미만의 값으로 오버플로되어서는 안됩니다. 종이에 비트 산술을 할 때 8 비트를 부호 비트로 생각하지 않습니다.

나는 비트 레벨에서 많은 정수로 작업하고, 모든 것이 부호를 갖 으면 일반적으로 모든 것이 직관적이지 않고 다루기가 더 어려워집니다. 음수를 오른쪽으로 이동하면 숫자가 새로운 것을 의미한다는 것을 기억해야하기 때문 1입니다. 부호없는 정수를 오른쪽으로 이동하면 결코 그렇게하지 않습니다. 예를 들면 다음과 같습니다.

signed byte b = 0b10000000;
b = b >> 1; // b == 0b1100 0000
b = b & 0x7F;// b == 0b0100 0000

unsigned byte b = 0b10000000;
b = b >> 1; // b == 0b0100 0000;

필요하지 않은 추가 단계를 추가합니다.

byte위에서 사용했지만 32 비트 및 64 비트 정수에도 동일하게 적용됩니다. 가지고 unsigned있지 않은 것은 자바와 같은 언어가 전혀 허용하지 않는 고급 언어가 있다는 사실에 충격을줍니다. 그러나 많은 프로그래머들이 비트 수준 산술을 다루지 않기 때문에 대부분의 사람들에게 이것은 문제가 아닙니다.

결국 부호없는 정수를 비트로 생각하면 부호없는 정수를 사용하는 것이 좋으며, 숫자로 생각할 때 부호있는 정수를 사용하는 것이 유용합니다.


7
서명되지 않은 정수 유형 (특히 바이트)이없는 언어에 대한 좌절감을 공유하지만 이것이 여기에 묻는 질문에 대한 직접적인 대답이 아닌 것 같습니다. 어쩌면 다음과 같은 결론을 추가 할 수있을 것입니다.“값으로 생각하면 부호없는 정수를 사용하고 숫자로 생각하면 부호있는 정수를 사용하십시오.”
5gon12eder

1
위의 의견에서 내가 말한 것입니다. 다른 사람이 같은 방식으로 생각하는 것을 보게되어 기쁩니다.
robert bristow-johnson 2012
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.