.NET에서 값 유형 (C # struct
)은 매개 변수가없는 생성자를 가질 수 없습니다. 이 게시물 에 따르면 이것은 CLI 사양에 의해 요구됩니다. 모든 값 유형에 대해 기본 생성자가 (컴파일러에 의해) 생성되어 모든 멤버를 0으로 초기화합니다 (또는 null
).
이러한 기본 생성자를 정의 할 수없는 이유는 무엇입니까?
한 가지 사소한 용도는 유리수입니다.
public struct Rational {
private long numerator;
private long denominator;
public Rational(long num, long denom)
{ /* Todo: Find GCD etc. */ }
public Rational(long num)
{
numerator = num;
denominator = 1;
}
public Rational() // This is not allowed
{
numerator = 0;
denominator = 1;
}
}
현재 버전의 C #을 사용하면 기본 Rational이 0/0
그리 좋지 않습니다.
추신 : 기본 매개 변수가 C # 4.0 에서이 문제를 해결하는 데 도움이됩니까? 또는 CLR 정의 기본 생성자가 호출됩니까?
Jon Skeet 가 대답했습니다.
당신의 모범을 사용하기 위해 누군가가했을 때 어떤 일이 일어나고 싶습니까?
Rational[] fractions = new Rational[1000];
생성자를 1000 번 실행해야합니까?
물론, 이것이 기본 생성자를 처음에 쓴 이유입니다. 명시 적 기본 생성자가 정의되지 않은 경우 CLR은 기본 제로 생성자를 사용해야합니다 . 그렇게하면 사용한만큼만 지불합니다. 그런 다음 기본이 아닌 1000 개의 컨테이너를 Rational
원하고 1000 구성을 최적화하려는 List<Rational>
경우 배열 대신 오히려 사용합니다 .
내 생각에이 이유는 기본 생성자의 정의를 막기에 충분하지 않다.
답변
참고 : 아래 답변은 C # 6 이전에 오래 전에 작성 되었습니다. 이 기능 은 C # 6에 추가되지 않았습니다 .
편집 : Grauenwolf의 CLR에 대한 통찰력으로 인해 아래 답변을 편집했습니다.
CLR을 사용하면 값 형식에 매개 변수가없는 생성자가있을 수 있지만 C #에는 없습니다. 나는 이것이 생성자가 그렇지 않을 때 호출 될 것이라는 기대를 불러 일으킬 것이라고 생각합니다. 예를 들어 다음을 고려하십시오.
MyStruct[] foo = new MyStruct[1000];
CLR은 적절한 메모리를 할당하고 모두 제로화하여이 작업을 매우 효율적으로 수행 할 수 있습니다. MyStruct 생성자를 1000 번 실행해야한다면 효율성이 훨씬 떨어집니다. (사실, 그것은하지 않습니다 – 당신이 경우에 할 매개 변수없는 생성자가 당신이 배열을 만들 때이 실행되지 않습니다, 또는 초기화되지 않은 인스턴스 변수가있을 때.)
C #의 기본 규칙은 “모든 유형의 기본값은 초기화에 의존 할 수 없습니다”입니다. 이제 그들은 할 수 있었다 매개 변수가없는 생성자를 정의 할 수 있습니다,하지만 생성자는 모든 경우에 수행 될 필요는 없다 -하지만 더 혼란을 주도했을 것이다. (또는 적어도 논쟁의 여지가 있다고 생각합니다.)
편집 : 귀하의 예를 사용하기 위해 누군가가했을 때 어떻게되고 싶습니까?
Rational[] fractions = new Rational[1000];
생성자를 1000 번 실행해야합니까?
- 그렇지 않다면, 1000 개의 유효하지 않은 합리적 결과를 얻게됩니다
- 그렇다면 배열을 실제 값으로 채우려 고하면 많은 작업을 낭비했을 것입니다.
편집 : (질문이 조금 더 있습니다) 매개 변수가없는 생성자는 컴파일러에 의해 생성되지 않습니다. 이 밝혀 있지만,이 – 값 유형은 멀리 CLR에 관한 한 같은 생성자를 할 필요는 없습니다 수 는 일리노이을 작성하는 경우. new Guid()
C #에서 ” ” 를 작성할 때 일반 생성자를 호출하면 얻을 수있는 것과 다른 IL을 방출합니다. 해당 측면에 대한 자세한 내용은 이 SO 질문 을 참조하십시오 .
나는 의심 매개 변수가없는 생성자와 프레임 워크의 모든 값 유형이 아니라는 것을. 의심 할 여지없이 NDepend가 충분히 훌륭하게 요구했는지 말해 줄 수 있습니다 … C #이 금지한다는 사실은 아마도 그것이 나쁜 생각이라고 생각하기에 충분히 큰 힌트 일 것입니다.
답변
구조체는 값 형식이며 값 형식은 선언되는 즉시 기본값을 가져야합니다.
MyClass m;
MyStruct m2;
인스턴스화하지 않고 위와 같이 두 개의 필드를 선언하면 디버거를 중단하면 m
null이되지만 m2
그렇지 않습니다. 이것을 감안할 때 매개 변수가없는 생성자는 의미가 없습니다. 실제로 구조체의 모든 생성자는 값을 할당하는 것입니다. 실제로 m2는 위의 예에서 아주 행복하게 사용될 수 있으며, 해당되는 경우 해당 메소드와 필드 및 속성을 조작 할 수 있습니다!
답변
CLR에서 허용하지만 C #에서는 구조체에 기본 매개 변수없는 생성자가있을 수 없습니다. 그 이유는 값 유형의 경우 기본적으로 컴파일러가 기본 생성자를 생성하지 않으며 기본 생성자에 대한 호출도 생성하지 않기 때문입니다. 따라서 기본 생성자를 정의한 경우에도 호출되지 않으며 혼동 될뿐입니다.
이러한 문제를 피하기 위해 C # 컴파일러는 사용자가 기본 생성자를 정의 할 수 없습니다. 또한 기본 생성자를 생성하지 않으므로 필드를 정의 할 때 필드를 초기화 할 수 없습니다.
또는 가장 큰 이유는 구조가 값 유형이고 값 유형이 기본값으로 초기화되고 생성자가 초기화에 사용되기 때문입니다.
당신은 당신의 구조체를 인스턴스화 할 필요가 없습니다. new
키워드로 . 대신 int처럼 작동합니다. 직접 액세스 할 수 있습니다.
구조체에는 명시적인 매개 변수없는 생성자가 포함될 수 없습니다. 구조 부재는 자동으로 기본값으로 초기화됩니다.
구조체의 기본 (매개 변수없는) 생성자는 예상치 못한 동작 인 0으로 끝나는 상태와 다른 값을 설정할 수 있습니다. 따라서 .NET 런타임은 구조체의 기본 생성자를 금지합니다.
답변
기본 “합리적”숫자를 초기화하고 반환하는 정적 속성을 만들 수 있습니다.
public static Rational One => new Rational(0, 1);
그리고 그것을 다음과 같이 사용하십시오 :
var rat = Rational.One;
답변
더 짧은 설명 :
C ++에서 struct와 class는 같은 동전의 양면에 불과했습니다. 유일한 차이점은 하나는 기본적으로 공개되었고 다른 하나는 비공개였습니다.
.NET 에서는 구조체와 클래스 사이에 훨씬 큰 차이가 있습니다. 가장 중요한 것은 구조체가 값 형식 의미를 제공하는 반면 클래스는 참조 형식 의미를 제공한다는 것입니다. 이 변경의 의미에 대해 생각하기 시작하면 설명하는 생성자 동작을 포함하여 다른 변경도 더 의미가 있습니다.
답변
나는 내가 줄 늦은 솔루션과 동등한 것을 보지 못 했으므로 여기에 있습니다.
오프셋을 사용하여 기본값 0에서 원하는 값으로 값을 이동하십시오. 여기서는 필드에 직접 액세스하는 대신 속성을 사용해야합니다. (가능한 c # 7 기능을 사용하면 속성 범위 필드를 더 잘 정의하여 코드에서 직접 액세스되지 않도록 할 수 있습니다.)
이 솔루션은 값 유형 만있는 간단한 구조체 (ref 유형 또는 nullable 구조체 없음)에 적합합니다.
public struct Tempo
{
const double DefaultBpm = 120;
private double _bpm; // this field must not be modified other than with its property.
public double BeatsPerMinute
{
get => _bpm + DefaultBpm;
set => _bpm = value - DefaultBpm;
}
}
이것은 다른 것보다 이 방법은 특별한 케이스하지만이 모든 범위에 대해 작동되는 오프셋 사용하지 않는,이 답변.
열거 형을 필드로 사용하는 예입니다.
public struct Difficaulty
{
Easy,
Medium,
Hard
}
public struct Level
{
const Difficaulty DefaultLevel = Difficaulty.Medium;
private Difficaulty _level; // this field must not be modified other than with its property.
public Difficaulty Difficaulty
{
get => _level + DefaultLevel;
set => _level = value - DefaultLevel;
}
}
내가 말했듯이 struct에 값 필드 만있는 경우 에도이 트릭이 모든 경우에 작동하지는 않을 수 있습니다. 그냥 검사하십시오. 그러나 당신은 일반적인 생각을 얻습니다.
답변
특별한 경우입니다. 분자가 0이고 분모가 0이면 실제로 원하는 값을 갖는 것처럼 가장하십시오.