나는이 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
합니다..
알아낼 수 있도록 도와주세요.
- 이것은 무엇을 의미합니까?
- 이것은 사용자 컨트롤이며 여러 개체가 레이블을 설정하기 위해 액세스하지 않으므로 코드를 그대로 유지해도 영향을 미치지 않습니다.
답변
이것이 의미하는 것은
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의 배열입니다. 따라서 할당 또는 쓰기에서 예외가 발생합니다.
답변
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>
IEnumerable
IEnumerable<Control>
답변
가장 직접적인 “솔루션”
flPanel.Controls.AddRange(_list.AsEnumerable());
이제 공변 적으로 변경 List<LinkLabel>
하기 IEnumerable<Control>
때문에 항목을 열거 가능 항목에 “추가”할 수 없으므로 더 이상 걱정할 필요가 없습니다.
답변
경고 때문에 당신이 이론적으로 추가 할 수 있다는 사실이다 Control
(A)보다 기타를 LinkLabel
받는 LinkLabel[]
관통 Control[]
그것을 참조. 이로 인해 런타임 예외가 발생합니다.
변환 때문에 여기에 무슨 일이 일어나고 AddRange
합니다 Control[]
.
보다 일반적으로 파생 된 유형의 컨테이너를 기본 유형의 컨테이너로 변환하는 것은 나중에 간략하게 설명 된 방식으로 컨테이너를 수정할 수없는 경우에만 안전합니다. 어레이는 해당 요구 사항을 충족하지 않습니다.
답변
문제의 근본 원인은 다른 답변에 올바르게 설명되어 있지만 경고를 해결하기 위해 항상 다음과 같이 쓸 수 있습니다.
_list.ForEach(lnkLbl => flPanel.Controls.Add(lnkLbl));
답변
VS 2008에서는이 경고가 표시되지 않습니다. .NET 4.0에 새로운 것이어야합니다.
설명 : Sam Mackrill에 따르면 경고를 표시하는 사람은 Resharper입니다.
C # 컴파일러는 AddRange
전달 된 배열을 수정 하지 않습니다 . AddRange
유형의 매개 변수가 있기 때문에 Control[]
이론적 TextBox
으로는 배열에 a를 할당하려고 시도 할 수 있습니다.이 배열은의 실제 배열에 완벽하게 맞지만 Control
배열은 실제로 배열이며 LinkLabels
이러한 할당을 수락하지 않습니다.
C #에서 배열을 공변량으로 만드는 것은 Microsoft의 잘못된 결정이었습니다. 파생 형식의 배열을 기본 형식의 배열에 처음 할당하는 것이 좋은 생각이지만 런타임 오류가 발생할 수 있습니다!
답변
이건 어때요?
flPanel.Controls.AddRange(_list.OfType<Control>().ToArray());