Array가 제네릭 유형이 아닌 이유는 무엇입니까?
Array
선언 :
public abstract class Array
: ICloneable, IList, ICollection, IEnumerable {
왜 그런지 궁금합니다.
public partial class Array<T>
: ICloneable, IList<T>, ICollection<T>, IEnumerable<T> {
제네릭 유형으로 선언 된 경우 문제는 무엇입니까?
제네릭 유형이라면 여전히 비 제네릭 유형이
Array<T>
필요하십니까? 아니면 파생 될 수 있습니까? 같은public partial class Array: Array<object> {
역사
배열이 제네릭 유형이 발생하면 어떤 문제가 발생합니까?
C # 1.0에서는 주로 Java에서 배열 개념을 복사했습니다. 그 존재하는 Generics는 존재하지 않는 제작자는 그들이 똑똑한 생각하고 Java 배열이 가진 깨진 공변 배열 의미를 복사했습니다. 즉, 동일한 타임 타임 오류 다음과 같은 작업을 수행 할 수 있습니다.
Mammoth[] mammoths = new Mammoth[10];
Animal[] animals = mammoths; // Covariant conversion
animals[1] = new Giraffe(); // Run-time exception
C # 2.0에서는 제네릭이 제네릭이 도입 제네릭 형식은 없습니다. 배열은 당신은 설치하지 않는 경우 Mammoth[]
에 Animal[]
, (이 모든에도 불구하고) 전에 할 수있는 일을합니다. 따라서 배열을 일반화 하면 많은 코드 가 손상되는 것 입니다.
C # 4.0에서만 도입 된 인터페이스에 대한 공변 / 반변 제네릭 형식이 함수. 통해 번 깨진 배열 공분산을 한 번에 고칠 수 있습니다. 그러나 다시 말하지만 이것은 많은 기존 코드를 깨 뜨렸을 것입니다.
Array<Mammoth> mammoths = new Array<Mammoth>(10);
Array<Animal> animals = mammoths; // Not allowed.
IEnumerable<Animals> animals = mammoths; // Covariant conversion
배열은 일반 인터페이스를 구현합니다.
왜 배열 일반적인 구현하지 않는
IList<T>
,ICollection<T>
그리고IEnumerable<T>
인터페이스를?
배열은 런타임 모든 트릭 덕분 T[]
않는 구현 IEnumerable<T>
, ICollection<T>
및 IList<T>
자동. 1 위 개인 Array
클래스에 대한 설명 :
단일 차원 배열은 구현
IList<T>
,ICollection<T>
,IEnumerable<T>
,IReadOnlyList<T>
및IReadOnlyCollection<T>
일반적인 인터페이스를. 구현은 배열에 제공되는 결과적으로 일반 인터페이스는 배열 클래스의 선언 구문에 배열에 제공됩니다.
배열로 구현 된 인터페이스의 모든 멤버를 사용할 수 있습니까?
아니. 문서는 다음과 같이 계속됩니다.
인터페이스 중 하나로 배열을 캐스트 할 때 필요한 핵심 사항은 요소를 추가, 삽입 또는 제거하는 멤버가
NotSupportedException
.
때문에 (예를 들어) 그의 ICollection<T>
이 Add
방법을,하지만 당신은 배열에 무엇을 추가 할 수 없습니다 . 예외가 발생합니다. 다음은 예외에서 발생하는 .NET Framework의 초기 디자인 오류의 또 다른 예입니다.
ICollection<Mammoth> collection = new Mammoth[10]; // Cast to interface type
collection.Add(new Mammoth()); // Run-time exception
그리고 ICollection<T>
공변이 아니기 때문에 (분명한 M) 이렇게 할 수 없습니다.
ICollection<Mammoth> mammoths = new Array<Mammoth>(10);
ICollection<Animal> animals = mammoths; // Not allowed
이제 후드 1 아래 에서 배열에 의해 구현되는 공변 IReadOnlyCollection<T>
인터페이스 가 물론 사용이 제한되어 있습니다.Count
기본 클래스 Array
배열이 제네릭이라면 여전히 비 제네릭
Array
클래스 가 필요하십니까?
초기에 우리는 그랬습니다. 모든 배열은 제네릭이 아닌 구현 , 그리고 자신의 기본 클래스를 통해 인터페이스를 . 이것은 모든 배열에 특정 방법과 인터페이스를 제공하는 유일한 합리적인 방법과 기본 클래스 의 기본 사용 방법입니다 . 열거에 동일한 선택을 볼 수 있습니다. 어느 쪽 값 유형이지만 ; 및 .IList
ICollection
IEnumerable
Array
Array
Enum
MulticastDelegate
Array
제네릭이 아닌 기본 클래스 를 제거 할 수 있습니까?
예, 모든 배열이 공유하는 메서드와 인터페이스 Array<T>
는 존재하는 경우 제네릭 클래스 에서 정의 할 수 있습니다 . 그런 다음 예를 들어 형식 기호의 추가 기능 Copy<T>(T[] source, T[] destination)
대신에 사용할 수 Copy(Array source, Array destination)
있습니다.
언어 지향 프로그래밍 관점에서 요소의 유형에 관계없이 모든 배열 을 참조하는 데 사용할 수 있는 비 제네릭 기본 클래스를 사용 하는 것이 좋습니다 . (일부 LINQ 메서드에서 여전히 사용되는) 상속 방법과 .Array
IEnumerable<T>
IEnumerable
수
Array
기본 클래스에서 파생Array<object>
?
아니요, 순환이 생성됨 Array<T> : Array : Array<object> : Array : ...
.. 또한 배열에 모든 속 배열 을 의미합니다. (결국 모든 배열은 의미 합니다 Array<object>
.)
미래
Array<T>
기존 코드에 너무 많은 영향을주지 않고 새로운 일반 배열 유형 을 추가 할 수 있습니까?
아니요. 구문은 사용할 수 있도록 만들 수 없습니다 배열 공분산은 사용할 수 없습니다.
배열은 .NET의 특수 유형입니다. Common Intermediate Language로 자체적으로 지시 할 수 있습니다. .NET 및 C # 디자이너 가이 길을 가기로 결정한 경우 T[]
구문 구문 설탕을 만들 수 있으며 Array<T>
( T?
Syntactic sugar for Nullable<T>
) 와 많은 메모리에 배열을 연속적으로 할당하는 특수 지침과 지원을 사용할 수 있습니다.
으로 캐스트 그러나 할 수없는 것과 유사하게 배열을 Mammoth[]
기본 유형 중 하나로 캐스트하는 기능 현관을 잃게 됩니다 . 그러나 배열 공분산은 어쨌든 깨졌고 더 나은 대안이 있습니다.Animal[]
List<Mammoth>
List<Animal>
배열 공분산의 대안?
모든 배열은 IList<T>
. IList<T>
가 적절한 공변 인터페이스 인터페이스로 만들어진 경우 모든 배열 Array<Mammoth>
(또는 해당 문제에 대한 목록)을 IList<Animal>
. 그러나 이렇게 많은 IList<T>
배열을 사용하는 방법을 제거하기 위해 인터페이스를 다시 작성해야합니다.
interface IList<out T> : ICollection<T>
{
T this[int index] { get; }
int IndexOf(object value);
}
interface ICollection<out T> : IEnumerable<T>
{
int Count { get; }
bool Contains(object value);
}
(위치의 매개 입력 입력 변수 유형은 T
이것이 공분산을 깨뜨리는 것과 같을 수 없습니다 . 그러나 잘못된 유형의 object-를 전달할 때 반환 되는 및 에게는
object
충분 합니다. 인터페이스를 구현 이러한하는 컬렉션은 고유 한 또는 일반 및 .)Contains
IndexOf
false
IndexOf(T value)
Contains(T value)
그런 다음 이렇게 할 수 있습니다.
Array<Mammoth> mammoths = new Array<Mammoth>(10);
IList<Animals> animals = mammoths; // Covariant conversion
배열 요소의 배열 요소의 유형이 배열되어 있는지 여부를 확인할 수 있기 때문에 성능이 약간 향상됩니다.
내 찌르기
설명한 위에서 Array<T>
실제 공변량 IList<T>
및 ICollection<T>
인터페이스 와 결합하여 C # 및 .NET 구현 될 경우으로 이러한 유형이 어떻게 작동 하는지 꼼꼼히 살펴 보았습니다. 또한 내 새로운 인터페이스 와 인터페이스에없는 돌연변이 메소드를 제공하기 위해 불변 IMutableList<T>
및 IMutableCollection<T>
인터페이스를 추가했습니다 .IList<T>
ICollection<T>
중심으로 간단한 컬렉션 라이브러리를 구축 하고 BitBucket에서 소스 코드를 사용하여 바이너리를 다운로드 하거나 NuGet 패키지를 사용할 수 있습니다.
M42.Collections – 기본 제공 .NET 컬렉션 클래스보다 더 많은 기능, 기능 및 사용 편의성을 갖춘 전문 컬렉션입니다.
1 ) 배열 T[]
닷넷의 기본 클래스 구현을 통해 4.5 Array
: , , , , , ; 및 자동 실행을 통해 : , , , ,와 .ICloneable
IList
ICollection
IEnumerable
IStructuralComparable
IStructuralEquatable
IList<T>
ICollection<T>
IEnumerable<T>
IReadOnlyList<T>
IReadOnlyCollection<T>
[업데이트, 새로운 담당자, 지금까지 뭔가 빠진 것 같았습니다.]
이전 답변과 관련하여 :
- 배열은 다른 유형과 많은 공변합니다. 'object [] foo = new string [5];'과 같은 것을 구현할 수 있습니다. 공분산으로 인해 그게 이유가 아닙니다.
- 아마도 디자인을 재고하지 않는 이유 일 것입니다. 그러나 나는 이것이 또한 정답이 아니라고 주장합니다.
그러나 내가 생각할 수있는 또 다른 이유는 배열이 메모리의 선형 요소 집합에 대한 '기본 유형'이기 때문입니다. 나는 배열 <T>를 사용하는 것에 대해 생각하고 있는데, T가 왜 대상이고 왜이 '대상'이 존재하는지 궁금 할 수도 있습니다. 이 시나리오에서 T []는 Array와 공변하는 Array <T>의 또 다른 구문입니다. 유형이 실제로 다르기 때문에 두 경우가있을 것이 생각합니다.
기본과 기본 배열은 모두 OO 언어의 요구 사항이 아닙니다. C ++가 이에 대한 완벽한 예입니다. 더 많은 배치 나 구조로 작업 할 수 있습니다. 사물의 경우 '객체'를 자연스럽게 느끼게하는 Foo 사물을 만드는 데 익숙합니다. 배열 클래스 기본가 시설 Foo를 수행하는 것도 똑같이 불가능합니다. 이는 자주 사용되지는 않지만 패러다임에 똑같이 중요합니다.
따라서 C #을 Array 기본 유형없이 사용하지만 실행 유형 (대부분의 리플렉션)이 풍부하면 IMO가 불가능합니다.
자세한 내용은 ...
어레이는 어디에 사용하고 왜 어레이입니까?
기본 유형을 사용하는 경우에는 대부분의 경우 사용이 가능합니다.
- 단순 배열
예, 우리는 이미 사람들이 사용하는 T[]
것처럼 List<T>
. 모두 정확하기 위하여, 인터페이스의 공통 집합을 구현 : IList<T>
, ICollection<T>
, IEnumerable<T>
, IList
, ICollection
와 IEnumerable
.
보유 알고 있다면 배열을 만들 수 있습니다. 우리는 또한 그것이 사실이라는 것을 알고 흥미롭지 계속 진행하고 있습니다.
- 컬렉션을 만듭니다.
리스트를 파헤쳐 보면 결국 Array로 끝날 것입니다. 정확히 말하면 T [] 배열입니다.
그 이유는 무엇입니까? 포인터 구조 (LinkedList)를 사용할 수 있습니다. 목록은 연속적인 메모리 블록이며 연속적인 메모리 블록이 됨 속도를 얻습니다. 여기에는 많은 이유가 간단하게 말하면 연속 메모리를 처리하는 것이 메모리를 처리하는 가장 빠른 방법입니다. CPU는 더 빨리 만드는 명령도 있습니다.
주의 깊은 독자는이를 위해 배열이 필요하지 않지만 IL이 이해하고 처리 할 수있는 'T'유형 요소의 연속 블록이 필요하다는 사실을 지적 할 수 있습니다. 즉, IL에서 동일한 작업을 수행하는 데 사용할 수있는 다른 유형이 있는지 확인하는 한 한 가지 어레이를 제거 할 수 있습니다.
값과 클래스 유형이 있습니다. 최상의 성능을 유지하려면 블록에 저장해야하지만 마샬링을 위해서는 단순히 요구 사항입니다.
- 마샬링.
마샬링은 모든 언어가 의사 소통에 동의하는 기본 유형을 사용합니다. 이러한 기본 유형은 byte, int, float, pointer ... 및 array와 같은 것입니다. 특히 C / C ++에서 배열이 사용되는 방식은 다음과 같습니다.
for (Foo *foo = beginArray; foo != endArray; ++foo)
{
// use *foo -> which is the element in the array of Foo
}
기본적으로 이것은 배열의 시작 부분에 포인터를 설정하고 배열의 끝에 도달 할 때까지 포인터 (sizeof (Foo) 바이트)를 증가시킵니다. 요소는 * foo에서 검색되며 포인터 'foo'가 가리키는 요소를 가져옵니다.
값 유형과 참조 유형이 있습니다. 단순히 모든 것을 객체로 박스에 저장하는 MyArray를 원하지는 않습니다. MyArray를 구현하는 것은 훨씬 더 까다로워졌습니다.
일부주의 깊은 독자들은 여기에 배열이 실제로 필요하지 않다는 사실을 지적 할 수 있습니다. 이는 사실입니다. Foo 유형의 연속 요소 블록이 필요하며 값 유형 인 경우 블록에 값 유형 (의 바이트 표현)으로 저장해야합니다.
- 다차원 배열
그래서 더 ... 다차원 성은 어떻습니까? 분명히 규칙은 그렇게 흑백이 아닙니다. 갑자기 모든 기본 클래스가 더 이상 없기 때문입니다.
int[,] foo2 = new int[2, 3];
foreach (var type in foo2.GetType().GetInterfaces())
{
Console.WriteLine("{0}", type.ToString());
}
강력한 유형은 방금 창 밖으로 나갔고 컬렉션 유형 IList
, ICollection
및 IEnumerable
. 이봐, 그럼 우리가 사이즈를 어떻게 구해야 돼? Array 기본 클래스를 사용할 때 다음을 사용할 수 있습니다.
Array array = foo2;
Console.WriteLine("Length = {0},{1}", array.GetLength(0), array.GetLength(1));
...하지만 같은 대안을 살펴보면 IList
동등한 것이 없습니다. 이 문제를 어떻게 해결할까요? IList<int, int>
여기에 소개할까요 ? 기본 유형은 int
. 어때 IMultiDimentionalList<int>
? 그렇게하고 현재 Array에있는 메서드로 채울 수 있습니다.
- 배열의 크기는 고정되어 있습니다.
어레이 재 할당에 대한 특별한 호출이 있다는 것을 알고 계셨습니까? 이것은 메모리 관리와 관련이 있습니다. 어레이는 너무 낮기 때문에 증가 또는 축소가 무엇인지 이해하지 못합니다. C에서는이를 위해 'malloc'과 'realloc'을 사용하고, 직접 할당 하는 모든 것에 대해 정확히 고정 된 크기를 갖는 것이 왜 중요한지 이해하려면 자신 만의 'malloc'과 'realloc'을 구현해야합니다 .
보시다시피 '고정 된'크기로 할당되는 것은 배열, 모든 기본 값 유형, 포인터 및 클래스입니다. 분명히 우리는 기본 유형을 다르게 처리하는 것처럼 배열을 다르게 처리합니다.
형식 안전에 대한 추가 정보
그렇다면 우선 이러한 모든 '액세스 포인트'인터페이스가 필요한 이유는 무엇입니까?
모든 경우에 가장 좋은 방법은 사용자에게 유형의 안전한 액세스 지점을 제공하는 것입니다. 이것은 다음과 같은 코드를 비교하여 설명 할 수 있습니다.
array.GetType().GetMethod("GetLength").Invoke(array, 0); // don't...
다음과 같이 코딩합니다.
((Array)someArray).GetLength(0); // do!
형식 안전을 사용하면 프로그래밍 할 때 엉성해질 수 있습니다. 올바르게 사용하면 컴파일러는 런타임을 찾는 대신 오류를 만든 경우 오류를 찾습니다. 이것이 얼마나 중요한지 충분히 강조 할 수는 없습니다. 결국 컴파일러가 항상 평가하는 동안 코드가 테스트 케이스에서 호출되지 않을 수 있습니다!
함께 모아서
그래서 .. 모두 합치 자. 우리는 다음을 원합니다.
- 강력한 형식의 데이터 블록
- 데이터가 지속적으로 저장됩니다.
- 빠른 출혈을 일으키는 멋진 CPU 명령을 사용할 수 있도록 IL 지원
- 모든 기능을 노출하는 공통 인터페이스
- 형식 안전성
- 다차원 성
- 값 유형이 값 유형으로 저장되기를 원합니다.
- 그리고 다른 언어와 동일한 마샬링 구조
- 고정 된 크기로 인해 메모리 할당이 더 쉬워집니다.
그것은 모든 컬렉션에 대해 상당히 낮은 수준의 요구 사항입니다. IL / CPU 로의 변환뿐만 아니라 특정 방식으로 구성되는 메모리가 필요합니다 ... 기본 유형으로 간주되는 좋은 이유가 있다고 말하고 싶습니다.
적합성. 배열은 제네릭이 없었던 시대로 거슬러 올라가는 역사적인 유형입니다.
이 나을 오늘해야 할 Array
다음, Array<T>
다음, 특정 클래스)
따라서 나는 그것이 아닌 이유를 알고 싶습니다.
그 이유는 제네릭이 C #의 첫 번째 버전에 없었기 때문입니다.
그러나 나는 스스로 문제가 무엇인지 알 수 없습니다.
문제는 Array
클래스 를 사용하는 엄청난 양의 코드를 깨뜨릴 것이라는 것 입니다. C #은 다중 상속을 지원하지 않으므로 다음과 같은 줄
Array ary = Array.Copy(.....);
int[] values = (int[])ary;
깨질 것입니다.
MS가 C #과 .NET을 처음부터 다시 만들고 Array
있다면 제네릭 클래스 를 만드는 데 아무런 문제가 없을 것입니다 . 그러나 그것은 현실이 아닙니다.
사람들이 언급 한 다른 문제 외에도 제네릭을 추가하려고 Array<T>
하면 몇 가지 다른 문제가 발생합니다.
제네릭이 도입 된 순간부터 오늘날의 공분산 기능이 존재 했더라도 어레이에는 충분하지 않았습니다. a를 정렬하도록 설계된 루틴 은 배열의 요소를 유형의 요소 로 복사 한 다음 다시 복사해야하는 경우에도
Car[]
를 정렬 할 수 있습니다 . 유형에서 다시 a로 요소를 복사 하는 것은 실제로 유형에 안전하지는 않지만 유용합니다. 정렬을 가능하게하는 방법 (예 :`Swap (int firstIndex, int secondIndex) method 포함)으로 공변 배열 1 차원 배열 인터페이스를 정의 할 수 있지만, 다음과 같이 유연한 것을 만드는 것은 어려울 수 있습니다. 배열입니다.Buick[]
Car
Car
Buick[]
동안
Array<T>
형태가 잘 작동 할 수도T[]
, 포함 할 것이다 가족 정의 제네릭 타입 시스템 내에서 아무런 의미가 없을 것T[]
,T[,]
,T[,,]
,T[,,,]
첨자의 임의의 번호 등..net에는 두 가지 유형이 동일한 것으로 간주되어야한다는 개념을 표현할 수있는 수단이 없습니다. 따라서 유형의 변수 는 동일한 객체에 대한 참조를 보유하는 두 변수를 사용하여 유형의 변수를 유형
T1
중 하나로 복사 할 수 있고T2
그 반대의 경우도 마찬가지입니다.Array<T>
유형을 사용하는 사람 은 예상하는 코드에 인스턴스를 전달 하고을 사용하는 코드T[]
에서 인스턴스를 수락 할 수 있기를 원할 것입니다T[]
. 새로운 스타일을 사용하는 코드에서 이전 스타일의 배열을 전달할 수 없다면 새로운 스타일의 배열은 기능보다 장애물이 될 것입니다.
유형 시스템을 징크 싱하는 방법이있을 수 Array<T>
있지만, 이러한 유형은 다른 제네릭 유형과는 완전히 다른 여러 가지 방식으로 작동 할 것입니다. 원하는 동작을 구현하는 유형이 이미 있기 때문입니다. (즉 T[]
) 다른 것을 정의함으로써 어떤 이점이 발생할지 명확하지 않습니다.
모두가 말했듯 Array
이 v1에서 제네릭이 없었기 때문에 원본 은 제네릭이 아닙니다. 아래 추측 ...
"배열"을 제네릭 (이제 의미가 있음)으로 만들려면 다음 중 하나를 수행 할 수 있습니다.
기존을 유지
Array
하고 일반 버전을 추가하십시오. 이것은 좋지만 "Array"의 대부분의 사용은 시간이 지남에 따라 증가하는 것과 관련이 있으며 동일한 개념의 더 나은 구현이List<T>
대신 구현 된 이유 일 가능성이 높습니다 . 이 시점에서 "증가하지 않는 요소의 순차 목록"의 일반 버전을 추가하는 것은 그다지 매력적이지 않습니다.비 제네릭을 제거 하고 동일한 인터페이스를
Array
가진 제네릭Array<T>
구현으로 대체하십시오 . 이제 기존Array
유형 대신 새로운 유형으로 작동하도록 이전 버전 용으로 컴파일 된 코드를 만들어야 합니다. 프레임 워크 코드가 이러한 마이그레이션을 지원하는 것이 가능할 수도 있지만 (또한 대부분 어려울 수 있음) 다른 사람이 작성한 코드는 항상 많습니다.Array
매우 기본적인 유형과 마찬가지로 기존 코드의 거의 모든 부분 (기본 코드 및 COM으로 리플렉션 및 마샬링하는 사용자 지정 코드 포함)이이를 사용합니다. 결과적으로 버전 (.Net Framework의 1.x-> 2.x)간에 약간의 비 호환성에 대한 가격도 매우 높습니다.
따라서 결과 Array
유형은 영원히 유지됩니다. 이제 우리는 List<T>
일반적인 등가물을 사용할 수 있습니다.
아마도 내가 뭔가를 놓치고 있지만 배열 인스턴스가 ICollection, IEnumerable 등으로 캐스팅되거나 사용되지 않는 한 T의 배열로 아무것도 얻지 못합니다.
배열은 빠르며 이미 형식이 안전하며 boxing / unboxing 오버 헤드가 발생하지 않습니다.
참조 URL : https://stackoverflow.com/questions/14324987/why-isnt-array-a-generic-type
'ProgramingTip' 카테고리의 다른 글
과학적 표기법없이 SQL 서버에서 float를 varchar로 변환 (0) | 2020.12.28 |
---|---|
Matplotlib- 일련의 선 전체에 컬러 바 추가 (0) | 2020.12.28 |
잠시하는 동안에 평가하는 Xcode (0) | 2020.12.28 |
Objective-C : NSURL에 쿼리 매개 변수를 추가하는 방법은 무엇입니까? (0) | 2020.12.28 |
ssl없이 npm 설치 (0) | 2020.12.28 |