x에서 y로 공변량 배열을 변환하면 런타임 예외가 발생할 수 있습니다


142

나는이 private readonly목록 LinkLabel들 ( IList<LinkLabel>). 나중에이 LinkLabel목록 에 s를 추가하고 해당 레이블을 FlowLayoutPanel다음과 같이 추가 합니다.

foreach(var s in strings)
{
    _list.Add(new LinkLabel{Text=s});
}

flPanel.Controls.AddRange(_list.ToArray());

Resharper는 다음과 같은 경고를 표시 Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation합니다..

알아낼 수 있도록 도와주세요.

  1. 이것은 무엇을 의미합니까?
  2. 이것은 사용자 컨트롤이며 여러 개체가 레이블을 설정하기 위해 액세스하지 않으므로 코드를 그대로 유지해도 영향을 미치지 않습니다.

답변:


154

이것이 의미하는 것은

Control[] controls = new LinkLabel[10]; // compile time legal
controls[0] = new TextBox(); // compile time legal, runtime exception

그리고 더 일반적인 용어로

string[] array = new string[10];
object[] objs = array; // legal at compile time
objs[0] = new Foo(); // again legal, with runtime exception

C #에서는 개체 배열 (이 경우 LinkLabels)을 기본 형식의 배열 (이 경우 Controls 배열)로 참조 할 수 있습니다. 또한 배열에 다른 객체 를 할당 하는 것이 합법적 인 컴파일 시간 Control입니다. 문제는 배열 이 실제로 컨트롤 배열 이 아니라는 것입니다. 런타임에 여전히 LinkLabels의 배열입니다. 따라서 할당 또는 쓰기에서 예외가 발생합니다.


귀하의 예와 같이 런타임 / 컴파일 시간 차이를 이해하지만 특수 유형에서 기본 유형으로 변환되지 않습니까? 또한 목록을 입력했으며 LinkLabel(전문 유형)에서 Control(기본 유형)으로갑니다.
TheVillageIdiot

2
예, LinkLabel에서 Control로 변환하는 것은 합법적이지만 여기서 일어나는 것과 동일하지 않습니다. 이것은에서 변환하는 방법에 대한 경고하고 LinkLabel[]Control[]여전히 합법적 인,하지만 런타임 문제가있을 수 있습니다. 변경된 것은 배열이 참조되는 방식입니다. 배열 자체는 변경되지 않습니다. 문제가 보입니까? 배열은 여전히 ​​파생 형식의 배열입니다. 참조는 기본 유형의 배열을 통해 이루어집니다. 따라서 기본 유형의 요소를 할당하는 것은 컴파일 시간에 합법적입니다. 그러나 런타임 유형은이를 지원하지 않습니다.
Anthony Pegram

귀하의 경우에는 문제가되지 않는다고 생각합니다. 배열을 사용하여 컨트롤 목록에 추가하는 것입니다.
Anthony Pegram

6
배열은 C #에서 wrongely 공변 왜 사람의 궁금하면 여기에 에릭 Lippert의의 설명 : 자바가와 CLR 디자이너는 언어 자바처럼 지원할 수 있기를 원해야하기 때문에 그것은 CLR에 추가되었습니다. 그런 다음 CLR에 있으므로 C #에 추가하고 추가했습니다. 이 결정은 당시에는 논란의 여지가 많았으므로 그 점에 대해 매우 만족 스럽지는 않지만 지금은 할 수있는 일이 없습니다.
franssu

14

Anthony Pegram의 답변을 명확히하려고 노력할 것입니다.

그것은 말했다 유형의 값 (예를 들어, 반환 할 때 일반 유형은 어떤 종류의 인수에 공변 인 Func<out TResult>의 반환 경우 TResult, IEnumerable<out T>수익률 인스턴스 T). 즉, 무언가가의 인스턴스를 반환하면 마치 인스턴스 인 TDerived것처럼 작업 할 수 있습니다 TBase.

제네릭 형식은 해당 형식의 값을 수락 할 때 (예 :의 Action<in TArgument>인스턴스를 수락 할 때) 일부 형식 인수에 대해 반 변형 적입니다 TArgument. 즉, 무언가의 인스턴스가 필요한 경우의 인스턴스를 TBase전달할 수도 있습니다 TDerived.

(예를 들어 일반 형식 서명에서 두 번 정의되지 않은 한) 일부 형식의 인스턴스를 허용하고 반환하는 일반 형식 CoolList<TIn, TOut>은 해당 형식 인수에 대해 공변량 또는 반 변형이 아닙니다. 예를 들어, List.NET 4 List<T>에서는 List<in T>또는 로 지정 되지 않습니다 List<out T>.

호환성 문제로 인해 Microsoft는 해당 인수를 무시하고 값 유형 인수에 대해 배열을 공 변형으로 만들었습니다. 어쩌면 그들은 분석을 수행하여 대부분의 사람들이 읽기 전용 인 것처럼 배열을 사용한다는 것을 알았습니다 (즉, 배열 초기화 장치를 사용하여 배열에 일부 데이터를 쓰는 경우). 따라서 장점은 런타임으로 인한 단점을 능가합니다. 누군가 배열에 쓸 때 공분산을 사용하려고 할 때 오류가 발생합니다. 따라서 허용되지만 권장되지는 않습니다.

원래 질문에 대해서는 원래 목록에서 복사 된 값 list.ToArray()으로 새로운 것을 만들고 LinkLabel[](합리적) 경고를 없애려면에 전달해야 Control[]합니다 AddRange. list.ToArray<Control>()작업을 수행합니다 : 인수로 ToArray<TSource>받아들이고 IEnumerable<TSource>리턴 TSource[]; 공분산 덕분에 인수로 받아들이는 메소드에 전달 될 수있는 List<LinkLabel>읽기 전용을 구현 합니다.IEnumerable<out LinkLabel>IEnumerableIEnumerable<Control>


11

가장 직접적인 "솔루션"

flPanel.Controls.AddRange(_list.AsEnumerable());

이제 공변 적으로 변경 List<LinkLabel>하기 IEnumerable<Control>때문에 항목을 열거 가능 항목에 "추가"할 수 없으므로 더 이상 걱정할 필요가 없습니다.


10

경고 때문에 당신이 이론적으로 추가 할 수 있다는 사실이다 Control(A)보다 기타를 LinkLabel받는 LinkLabel[]관통 Control[]그것을 참조. 이로 인해 런타임 예외가 발생합니다.

변환 때문에 여기에 무슨 일이 일어나고 AddRange합니다 Control[].

보다 일반적으로 파생 된 유형의 컨테이너를 기본 유형의 컨테이너로 변환하는 것은 나중에 간략하게 설명 된 방식으로 컨테이너를 수정할 수없는 경우에만 안전합니다. 어레이는 해당 요구 사항을 충족하지 않습니다.


5

문제의 근본 원인은 다른 답변에 올바르게 설명되어 있지만 경고를 해결하기 위해 항상 다음과 같이 쓸 수 있습니다.

_list.ForEach(lnkLbl => flPanel.Controls.Add(lnkLbl));

2

VS 2008에서는이 경고가 표시되지 않습니다. .NET 4.0에 새로운 것이어야합니다.
설명 : Sam Mackrill에 따르면 경고를 표시하는 사람은 Resharper입니다.

C # 컴파일러는 AddRange전달 된 배열을 수정 하지 않습니다 . AddRange유형의 매개 변수가 있기 때문에 Control[]이론적 TextBox으로는 배열에 a를 할당하려고 시도 할 수 있습니다.이 배열은의 실제 배열에 완벽하게 맞지만 Control배열은 실제로 배열이며 LinkLabels이러한 할당을 수락하지 않습니다.

C #에서 배열을 공변량으로 만드는 것은 Microsoft의 잘못된 결정이었습니다. 파생 형식의 배열을 기본 형식의 배열에 처음 할당하는 것이 좋은 생각이지만 런타임 오류가 발생할 수 있습니다!


2
나는 Resharper에서이 경고를받습니다
Sam Mackrill

1

이건 어때요?

flPanel.Controls.AddRange(_list.OfType<Control>().ToArray());

2
와 동일한 결과 _list.ToArray<Control>()입니다.
jsuddsjr
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.