구조체와 클래스를 언제 사용할 지에 대한 경험 규칙은 무엇입니까? 나는 그 용어에 대한 C # 정의를 생각하고 있지만 언어가 비슷한 개념을 가지고 있다면 당신의 의견도 듣고 싶습니다.
나는 거의 모든 것에 클래스를 사용하는 경향이 있으며 무언가가 매우 단순하고 PhoneNumber와 같은 값 유형이어야하는 경우에만 구조체를 사용합니다. 그러나 이것은 비교적 사소한 사용처럼 보이고 더 흥미로운 사용 사례가 있기를 바랍니다.
구조체와 클래스를 언제 사용할 지에 대한 경험 규칙은 무엇입니까? 나는 그 용어에 대한 C # 정의를 생각하고 있지만 언어가 비슷한 개념을 가지고 있다면 당신의 의견도 듣고 싶습니다.
나는 거의 모든 것에 클래스를 사용하는 경향이 있으며 무언가가 매우 단순하고 PhoneNumber와 같은 값 유형이어야하는 경우에만 구조체를 사용합니다. 그러나 이것은 비교적 사소한 사용처럼 보이고 더 흥미로운 사용 사례가 있기를 바랍니다.
답변:
따라야 할 일반적인 규칙은 구조체가 작고 간단한 (1 레벨) 관련된 속성 모음이어야하며, 일단 생성되면 변경할 수 없다는 것입니다. 다른 것을 위해, 수업을 사용하십시오.
C #은 구조체와 클래스가 정의 키워드 이외의 선언에서 명시적인 차이가 없다는 점에서 훌륭합니다. 따라서 구조체를 클래스로 "업그레이드"하거나 반대로 클래스를 구조체로 "다운 그레이드"해야한다고 생각되면 키워드를 변경하는 간단한 문제입니다 (기타 몇 가지 문제가 있습니다. 다른 클래스 또는 구조체 유형에서 기본 매개 변수가없는 생성자를 명시 적으로 정의 할 수 없습니다).
구조체에 대해 알아야 할 더 중요한 것은 값 타입이기 때문에 클래스 (참조 타입)처럼 취급하면 고통을 반으로 줄 수 있다는 것입니다. 특히 구조의 속성을 변경 가능하게하면 예기치 않은 동작이 발생할 수 있습니다.
예를 들어 A와 B라는 두 속성이있는 SimpleClass 클래스가 있다고 가정합니다.이 클래스의 복사본을 인스턴스화하고 A와 B를 초기화 한 다음 인스턴스를 다른 메서드에 전달합니다. 이 메서드는 A와 B를 추가로 수정합니다. 호출 함수 (인스턴스를 만든 함수)로 돌아 가면 인스턴스의 A와 B는 호출 된 메서드에 의해 주어진 값을 갖게됩니다.
자, 당신은 그것을 구조체로 만듭니다. 속성은 여전히 변경 가능합니다. 이전과 동일한 구문으로 동일한 작업을 수행하지만 이제는 메서드를 호출 한 후 A 및 B의 새 값이 인스턴스에 없습니다. 어떻게 된 거예요? 글쎄, 당신의 클래스는 이제 구조체입니다. 즉, 값 유형입니다. 값 유형을 메소드에 전달하는 경우, 기본값 (out 또는 ref 키워드없이)은 "값별"을 전달하는 것입니다. 인스턴스의 얕은 사본은 메소드에서 사용하기 위해 작성된 다음 메소드가 완료되면 초기 인스턴스는 그대로두고 폐기됩니다.
구조체의 멤버로 참조 유형을 가지고 있다면 이것은 더욱 혼란스러워집니다 ( 거의 모든 경우에 허용되지 않지만 매우 나쁜 연습). 클래스는 복제되지 않으며 (구조체에 대한 구조체 참조 만 해당) 구조체에 대한 변경은 원래 객체에 영향을 미치지 않지만 구조체의 하위 클래스에 대한 변경은 호출 코드의 인스턴스에 영향을 미칩니다. 이렇게하면 변경 가능한 구조체를 매우 일관되지 않은 상태로 만들어 실제 문제가있는 곳에서 멀리 떨어진 곳에서 오류를 일으킬 수 있습니다.
이러한 이유로 C #의 거의 모든 기관은 항상 구조를 변경할 수 없다고 말합니다. 소비자는 객체 구성시에만 속성 값을 지정할 수 있으며 해당 인스턴스 값을 변경할 수있는 수단을 제공하지 않습니다. 읽기 전용 필드 또는 가져 오기 전용 속성이 규칙입니다. 소비자가 값을 변경하려는 경우 원하는 변경 사항을 사용하여 이전 값을 기반으로 새 개체를 만들거나 동일한 방법으로 메서드를 호출 할 수 있습니다. 이것은 당신의 구조체의 하나의 인스턴스를 하나의 개념적 "가치"로, 불가분의, 그리고 다른 모든 것과는 구별 할 수있는 (그러나 아마도 동일 할 수있는) 하나의 개념적 "가치"로 취급하도록합니다. 유형별로 저장된 "값"에 대해 작업을 수행하면 초기 값과 다른 새로운 "값"을 얻습니다.
좋은 예를 보려면 DateTime 유형을보십시오. DateTime 인스턴스의 필드를 직접 지정할 수 없습니다. 새 인스턴스를 작성하거나 기존 인스턴스에서 새 인스턴스를 생성하는 메소드를 호출해야합니다. 날짜와 시간이 숫자 5와 같이 "값"이고 숫자 5를 변경하면 5가 아닌 새 값이 생성되기 때문입니다. 5 + 1 = 6이 5가 6이라는 의미는 아니기 때문에 1을 추가했기 때문에. DateTimes도 같은 방식으로 작동합니다. 12:00은 1 분을 추가하면 12:01이되지 않습니다. 대신 12:00과 다른 새 값 12:01을 얻습니다. 이것이 귀하의 유형에 대한 논리적 인 문제인 경우 (.NET에 내장되어 있지 않은 좋은 개념의 예는 돈, 거리, 무게 및 작업이 가치의 모든 부분을 고려해야하는 UOM의 다른 수량입니다), 그런 다음 구조체를 사용하고 적절하게 디자인하십시오. 객체의 하위 항목을 독립적으로 변경할 수있는 대부분의 경우 클래스를 사용하십시오.
대답은 "순수한 데이터 구조에 구조체를 사용하고 작업이있는 개체에 클래스를 사용하는 것"은 분명히 잘못된 IMO입니다. 구조체에 많은 수의 속성이 있으면 클래스가 거의 항상 더 적합합니다. Microsoft는 종종 효율성 관점에서 유형이 16 바이트보다 큰 경우 클래스 여야한다고 말합니다.
https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes
a class
와 a 의 가장 중요한 차이점 struct
은 다음 상황에서 발생합니다.
Thing thing1 = 새로운 Thing (); thing1.somePropertyOrField = 5; 일 thing2 = 일 1; thing2.somePropertyOrField = 9;
마지막 진술의 효과는 무엇입니까 thing1.somePropertyOrField
? 만약 Thing
구조체 및 somePropertyOrField
노출 공공 필드, 객체가 thing1
와 thing2
서로 "분리 된"입니다, 그래서 후자의 문이 영향을 미치지 않습니다 thing1
. 경우는 Thing
클래스이며, 다음 thing1
과 thing2
서로 연결되고, 그래서 후자의 문을 작성합니다 thing1.somePropertyOrField
. 전자의 의미가 더 의미가있는 경우에는 구조체를 사용해야하고, 후자의 의미가 더 의미가있는 경우에는 클래스를 사용해야합니다.
어떤 사람들은 어떤 것을 바꾸고 자하는 욕구가 클래스라는 점에서 유리한 주장이라고 조언하지만, 그 반대의 경우는 사실입니다. 만약 어떤 데이터를 보유 할 목적으로 존재하는 어떤 것이 변할 수 있다면, 인스턴스가 어떤 것에 연결되어 있는지 확실하지 않은 경우, 인스턴스가 다른 것에 연결되지 않았 음을 분명히하기 위해 (노출 된 필드가있는) 구조체 여야합니다.
예를 들어, 다음 문장을 고려하십시오.
Person somePerson = myPeople.GetPerson ( "123-45-6789"); somePerson.Name = "메리 존슨"; // "메리 스미스"
두 번째 진술서에 저장된 정보가 변경 myPeople
됩니까? 경우 Person
노출 된 필드 구조체는, 그것은하지 않습니다, 그리고 그것 인 노출 필드 구조체의 명백한 결과가 될 것입니다하지 않을 것이라는 사실 ; Person
구조체이고 업데이 트하려는 경우 myPeople
, 분명히 뭔가해야합니다 myPeople.UpdatePerson("123-45-6789", somePerson)
. Person
그러나 클래스 인 경우 위의 코드가의 내용을 MyPeople
업데이트하지 않거나 항상 업데이트하거나 때로는 업데이트 하는지 여부를 결정하기가 훨씬 어려울 수 있습니다 .
구조체가 "불변"이어야한다는 개념과 관련하여, 나는 일반적으로 동의하지 않습니다. "불변"구조체에 대한 유효한 사용 사례가 있지만 (변형이 생성자에서 시행되는 경우) 변경의 일부가 어색하고 낭비 적이며 단순히 노출하는 것보다 버그를 유발할 수있는 경우 항상 전체 구조체를 다시 작성해야합니다. 직접 필드. 예를 들어, 고려 PhoneNumber
, 그 필드 구조체를 다른 사람의 사이에서 포함 AreaCode
하고 Exchange
, 하나는 가지고 가정 List<PhoneNumber>
. 다음의 효과는 매우 분명해야합니다.
for (int i = 0; i <myList.Count; i ++) { PhoneNumber theNumber = myList [i]; if (theNumber.AreaCode == "312") { 문자열 newExchange = ""; if (new312to708Exchanges.TryGetValue (theNumber.Exchange), newExchange) { theNumber.AreaCode = "708"; theNumber.Exchange = newExchange; myList [i] = theNumber; } } }
위의 코드 PhoneNumber
에서는 AreaCode
및 이외의 필드를 아는 것이 없습니다 Exchange
. 경우 PhoneNumber
소위 "불변"구조체했다, 그것은해야 할 것 중 하나 그것은 제공하는 withXX
지정된 필드에 전달 된 값을 개최 새 구조체 인스턴스를 반환, 그렇지 않으면이 필요하다 각 필드에 대한 방법을, 위의 코드는 구조체의 모든 필드에 대해 알 수 있습니다. 정확히 호소력이 없습니다.
BTW에서 구조체가 가변 유형에 대한 참조를 합리적으로 보유 할 수있는 경우가 적어도 두 가지 있습니다.
전자의 시나리오에서 객체의 IDENT는 불변이다. 두 번째로, 내포 된 객체의 클래스는 불변성을 강요하지는 않지만 참조를 보유한 구조체는이를 유지합니다.
나는 하나의 메소드가 필요할 때만 구조체를 사용하는 경향이 있으므로 클래스를 너무 무겁게 보이게 만듭니다. 따라서 때로는 구조체를 경량 객체로 취급합니다. 주의 : 이것은 항상 작동하는 것은 아니며 항상 최선의 방법은 아니므로 상황에 따라 달라집니다!
추가 자료
더 공식적인 설명을 위해 MSDN은 다음과 같이 말합니다 : http://msdn.microsoft.com/en-us/library/ms229017.aspx 이 링크는 위에서 언급 한 내용을 확인하므로 구조체는 소량의 데이터를 저장하는 데만 사용해야합니다.
순수한 데이터 구조에는 구조체를 사용하고 연산이있는 객체에는 클래스를 사용하십시오.
데이터 구조에 액세스 제어가 필요없고 get / set 이외의 특수 작업이없는 경우 구조체를 사용하십시오. 이것은 모든 구조가 데이터의 컨테이너라는 것을 분명히합니다.
구조에 더 복잡한 방식으로 구조를 수정하는 작업이있는 경우 클래스를 사용하십시오. 클래스는 메소드에 대한 인수를 통해 다른 클래스와 상호 작용할 수도 있습니다.
그것을 벽돌과 비행기의 차이점으로 생각하십시오. 벽돌은 구조체입니다. 길이, 너비 및 높이가 있습니다. 별로 할 수 없습니다. 비행기는 제어 표면 이동 및 엔진 추력 변경과 같은 여러 가지 조작으로 비행기를 돌연변이시킬 수 있습니다. 수업으로 더 좋을 것입니다.