목록을 목록 으로 변환
클래스할 수 만, 「」/「」를 수 입니까?List<>
같은 클래스/인터페이스를 사용하고 있습니까?
interface A
{ }
class B : A
{ }
class C : B
{ }
class Test
{
static void Main(string[] args)
{
A a = new C(); // OK
List<A> listOfA = new List<C>(); // compiler Error
}
}
다른 방법이 있나요?
이 작품을 만드는 방법은 목록을 반복하고 요소를 캐스팅하는 것입니다.이 작업은 Convert를 사용하여 수행할 수 있습니다.모두:
List<A> listOfA = new List<C>().ConvertAll(x => (A)x);
Linq를 사용할 수도 있습니다.
List<A> listOfA = new List<C>().Cast<A>().ToList();
우선, A, B, C와 같이 이해하기 어려운 반 이름을 사용하지 마세요.동물, 포유동물, 기린 또는 음식, 과일, 오렌지 또는 관계가 분명한 것을 사용하세요.
그러면 여러분의 질문은 "왜 기린 목록을 동물 유형 목록에 지정할 수 없는가?" 입니다. 기린은 동물 유형 변수에 지정할 수 있는데 말이죠.
정답은: 할 수 있다고 가정해 보세요.그러면 무엇이 잘못될 수 있을까요?
호랑이를 동물 목록에 추가할 수 있습니다.동물 목록을 포함하는 변수에 기린 목록을 넣을 수 있습니다.그리고 당신은 그 목록에 호랑이를 추가하려고 합니다.어떻게 되는 거죠?당신은 기린 목록에 호랑이가 포함되기를 원하나요?한 잔 할래?아니면 컴파일러가 애초에 할당을 불법으로 함으로써 크래시로부터 당신을 보호하기를 원합니까?
우리는 후자를 택한다.
이런 종류의 변환을 "공변" 변환이라고 합니다.C# 4에서는 변환이 항상 안전한 것으로 알려진 경우 인터페이스 및 위임에서 공변 변환을 수행할 수 있습니다.자세한 것은, 공분산 및 위반에 관한 제 블로그 기사를 참조해 주세요.(이번 주 월요일과 목요일 모두 이 주제에 관한 새로운 기사가 있습니다.)
에릭의 훌륭한 설명을 인용하자면
어떻게 되는 거죠?당신은 기린 목록에 호랑이가 포함되기를 원하나요?한 잔 할래?아니면 컴파일러가 애초에 할당을 불법으로 함으로써 크래시로부터 당신을 보호하기를 원합니까?우리는 후자를 택한다.
하지만 컴파일 오류 대신 런타임 크래시를 선택하고 싶다면 어떻게 해야 할까요?통상, 캐스트<>또는 변환을 사용합니다.모두 << 고객명 >>님만, 다음의 2개의 문제가 발생합니다.리스트의 카피가 작성됩니다.새 목록에서 항목을 추가하거나 제거하면 원래 목록에 반영되지 않습니다.둘째, 기존 오브젝트를 사용하여 새 목록을 작성하기 때문에 퍼포먼스와 메모리에 큰 불이익이 발생합니다.
저도 같은 문제가 있어서 완전히 새로운 목록을 작성하지 않고 일반 목록을 캐스팅할 수 있는 래퍼 클래스를 만들었습니다.
첫 번째 질문에서는 다음을 사용할 수 있습니다.
class Test
{
static void Main(string[] args)
{
A a = new C(); // OK
IList<A> listOfA = new List<C>().CastList<C,A>(); // now ok!
}
}
여기서 래퍼 클래스(+는 사용하기 쉬운 확장 메서드 CastList)
public class CastedList<TTo, TFrom> : IList<TTo>
{
public IList<TFrom> BaseList;
public CastedList(IList<TFrom> baseList)
{
BaseList = baseList;
}
// IEnumerable
IEnumerator IEnumerable.GetEnumerator() { return BaseList.GetEnumerator(); }
// IEnumerable<>
public IEnumerator<TTo> GetEnumerator() { return new CastedEnumerator<TTo, TFrom>(BaseList.GetEnumerator()); }
// ICollection
public int Count { get { return BaseList.Count; } }
public bool IsReadOnly { get { return BaseList.IsReadOnly; } }
public void Add(TTo item) { BaseList.Add((TFrom)(object)item); }
public void Clear() { BaseList.Clear(); }
public bool Contains(TTo item) { return BaseList.Contains((TFrom)(object)item); }
public void CopyTo(TTo[] array, int arrayIndex) { BaseList.CopyTo((TFrom[])(object)array, arrayIndex); }
public bool Remove(TTo item) { return BaseList.Remove((TFrom)(object)item); }
// IList
public TTo this[int index]
{
get { return (TTo)(object)BaseList[index]; }
set { BaseList[index] = (TFrom)(object)value; }
}
public int IndexOf(TTo item) { return BaseList.IndexOf((TFrom)(object)item); }
public void Insert(int index, TTo item) { BaseList.Insert(index, (TFrom)(object)item); }
public void RemoveAt(int index) { BaseList.RemoveAt(index); }
}
public class CastedEnumerator<TTo, TFrom> : IEnumerator<TTo>
{
public IEnumerator<TFrom> BaseEnumerator;
public CastedEnumerator(IEnumerator<TFrom> baseEnumerator)
{
BaseEnumerator = baseEnumerator;
}
// IDisposable
public void Dispose() { BaseEnumerator.Dispose(); }
// IEnumerator
object IEnumerator.Current { get { return BaseEnumerator.Current; } }
public bool MoveNext() { return BaseEnumerator.MoveNext(); }
public void Reset() { BaseEnumerator.Reset(); }
// IEnumerator<>
public TTo Current { get { return (TTo)(object)BaseEnumerator.Current; } }
}
public static class ListExtensions
{
public static IList<TTo> CastList<TFrom, TTo>(this IList<TFrom> list)
{
return new CastedList<TTo, TFrom>(list);
}
}
「 」를 사용하고 IEnumerable
대신 동작합니다(적어도 C# 4.0에서는 이전 버전을 사용해 본 적이 없습니다).이건 그냥 캐스팅일 뿐이고, 물론 여전히 리스트가 될 것입니다.
대신 -
List<A> listOfA = new List<C>(); // compiler Error
질문의 원래 코드에는 -를 사용합니다.
IEnumerable<A> listOfA = new List<C>(); // compiler error - no more! :)
왜 효과가 없는지에 관한 한 공분산과 반차를 이해하는 것이 도움이 될 수 있습니다.
이것이 동작하지 않는 이유를 나타내기 위해서, 다음에 입력한 코드의 변경을 나타냅니다.
void DoesThisWork()
{
List<C> DerivedList = new List<C>();
List<A> BaseList = DerivedList;
BaseList.Add(new B());
C FirstItem = DerivedList.First();
}
이게 효과가 있을까요?목록의 첫 번째 항목은 "B" 유형이지만 DerivedList 항목의 유형은 C입니다.
A를 실장하는 타입의 리스트로 동작하는 범용 함수를 만들고 싶다고 합니다만, 그 타입이 어떤 것이든 상관없습니다.
void ThisWorks<T>(List<T> GenericList) where T:A
{
}
void Test()
{
ThisWorks(new List<B>());
ThisWorks(new List<C>());
}
읽기 전용 목록에만 캐스팅할 수 있습니다.예를 들어 다음과 같습니다.
IEnumerable<A> enumOfA = new List<C>();//This works
IReadOnlyCollection<A> ro_colOfA = new List<C>();//This works
IReadOnlyList<A> ro_listOfA = new List<C>();//This works
또한 요소 저장을 지원하는 목록에는 이 작업을 수행할 수 없습니다.이유는 다음과 같습니다.
List<string> listString=new List<string>();
List<object> listObject=(List<object>)listString;//Assume that this is possible
listObject.Add(new object());
그럼 어쩌라는 거야?listObject와 listString은 실제로 동일한 목록이기 때문에 listString에 오브젝트 요소가 있습니다.이러한 요소는 가능하지 않습니다.
고객님의 문제에는 몇 가지 네이티브 C#의 가능성이 있습니다.
IReadOnlyList
,IEnumerable
=> 오류는 없고, 활자는 안전합니다.아마 필요한 것.List<>
방법 => free / 방방 = > 오 types / typesafe 아 theArray
/ errors => "실행시 오류 발생" / "실행시 오류dynamic
★★★★★★★★★★★★★★★★★」List<object>
=> 범용 / 타입세이프가 아님 / 런타임 오류 발생
모두 잘 작동합니다!까다로운 프로그래밍은 필요 없습니다!
모든 예에 대한 인터페이스 및 클래스 정의:
using System; using System.Collections.Generic; using System.Linq;
interface IAnimal
{
public string Name { get; }
}
class Bear : IAnimal
{
public string BearName = "aBear";
public string Name => BearName;
}
class Cat : IAnimal
{
public string CatName = "aCat";
public string Name => CatName;
}
// Dog has no base class/interface; it isn't related to the other classes
// But it also provides a <Name> property (what a coincidence!)
class Dog
{
public string DogName = "aDog";
public string Name => DogName;
}
public class AssignDerivedClass {
private static string AnyName(dynamic anymal) => anymal switch
{
IAnimal animal => animal.Name,
Dog dog => dog.DogName,
string s => s,
_ => "Any unknown Animal"
};
각 솔루션의 예를 다음에 나타냅니다.
1. IReadOnlyList
,IEnumerable
한 것 : ★★★★★★★★★★★★★ ★
- [ ] 를 합니다.
List<C>
IEnumerable<A>
★★★★★★★★★★★★★★★★★」IReadOnlyList<A>
- 다 런타임에 할 수 . 즉,시 변경할 수
Add
★★★★★★★★★★★★★★★★★」Remove
★★★★★★★★★★★★★★★★★★. - 데이터 요소 자체는 수정할 수 있습니다.
- 「」를 해 주세요.
IEnumerable
★★★★★★★★★★★★★★★★★」IReadOnlyList
매우 저렴합니다.다른 포인터를 통해 데이터에 액세스합니다. - 단해 :, ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」
Append
IReadOnlyList로후 IEnumerable IReadOnlyList를 만듭니다.IEnumerable
해서.이것을사용해서IEnumerable
부적절하게 비싸질 수 있습니다.
요소를 추가할 수 없으므로 모든 요소는 올바른 유형으로 유지됩니다.
public static void TestIEnumerableAndIReadonlyList()
{
var cats = new List<Cat>()
{
new Cat() { CatName = "Cat-3" },
};
IEnumerable<IAnimal> animalsEnumerable = cats;
IReadOnlyList<IAnimal> animalsReadOnlyList = cats;
var extendedEnumerable = animalsReadOnlyList.Append(new Bear());
(extendedEnumerable.First() as Cat).CatName = "Cat-3a";
Console.WriteLine("Cat names: {0}, {1}, {2}, {3}",
cats.ElementAt(0).CatName,
animalsReadOnlyList[^1].Name,
AnyName(animalsEnumerable.Last()),
AnyName(extendedEnumerable.ElementAt(1)));
}
// Result => Cat names: Cat-3a, Cat-3a, Cat-3a, aBear
. 2. 용 2List<>
다음 중 하나: List<A> listOfA = new()
- 인터페이스 목록 정의(파생 클래스가 아님)
- 파생 클래스의 인스턴스만 할당 - 다른 클래스를 저장하려는 것은 아니었죠?
곰으로 추정되는 동물 목록에 고양이 추가:
public static void TestListOfInterface()
{
var bears = new List<IAnimal>()
{
new Bear() { BearName = "Bear-1" },
};
bears.Add(new Cat() { CatName = "Cat-2" });
string bearNames = string.Join(", ", bears.Select(animal => animal.Name));
Console.WriteLine($"Bear names: {bearNames}");
static string VerifyBear(IAnimal bear)
=> (bear as Bear)?.Name ?? "disguised as a bear!!!";
string bearInfo0 = VerifyBear(bears[0]);
string bearInfo1 = VerifyBear(bears[1]);
Console.WriteLine($"One animal is {bearInfo0}, the other one is {bearInfo1}");
}
// Bear names: Bear-1, Cat-2
// One animal is Bear-1, the other one is disguised as a bear!!!
3. Array
: 성성Bear[]
모든 "Discription", "Discription", "Discription", "Discription", "Discription", "Discription", "Discription", "Discription"의를 참조합니다.Bear
.
- 요소를 교환할 수는 있지만 새 요소를 제거하거나 추가할 수는 없습니다.
- 잘못된 유형을 설정하려고 하면 런타임 오류가 발생합니다.
배열 내 곰:
public static void TestArray()
{
Bear[] bears = { new Bear(), null };
IAnimal[] bearAnimals = bears;
try { bearAnimals[1] = new Cat(); } // => ArrayTypeMismatchException
catch (Exception e) { Console.Error.WriteLine(e); } // type incompatible with array
bearAnimals[1] = new Bear() { BearName = "Bear-2" };
Console.WriteLine($"Bear names: {bearAnimals[0].Name}, {bears[1].BearName}");
}
// Result => Bear names: aBear, Bear-2
4. dynamic
★★★★★★★★★★★★★★★★★」List<dynamic>
: 가장 보편적인 솔루션
- 실행 시 유형 검사
- 컴파일러 오류 체크 지원을 포기했으므로 취급에 주의해 주십시오.
- 잘못된 유형의 요소를 추가하려고 하면 런타임 오류만 발생합니다.
- 존재하지 않는 멤버에 액세스하면 런타임 오류만 발생합니다!
- 관련이 없는 클래스의 컬렉션을 할당할 수도 있습니다.
을 " " " 에 할당합니다.dynamic
「」를 사용합니다.List<dynamic>
:
public static void TestDynamicListAndArray()
{
dynamic any = new List<Cat>() // List of specific class - or of interface
{
new Cat() { CatName = "Cat-1" },
new Cat() { CatName = "Cat-2" },
};
try { any[0].BearName = "Bear-1"; } // => RuntimeBinderException
catch (Exception e) { Console.Error.WriteLine(e); } // Cat has no BearName
try { any.Add(new Bear()); } // => RuntimeBinderException
catch (Exception e) { Console.Error.WriteLine(e); } // no matching overload
any[1].CatName += 'a';
Console.WriteLine($"Animal names: {any[0].CatName}, {any[1].Name}");
var mix = new List<dynamic>
{
new Bear() {BearName = "Bear-3"},
new Dog() {DogName = "Dog-4"},
"Cat-5", // 100MHz ;-)
};
Console.WriteLine($"{AnyName(mix[0])}, {mix[1].Name}, {AnyName(mix[2])}");
try { Console.WriteLine($"Names: {any[2].Name}"); } // => RuntimeBinderException
catch (Exception e) { Console.Error.WriteLine(e); } // no definition for 'Name'
any = new Bear() { BearName = "Bear-6" }; // Scalar - not a List or Array!
try { Console.WriteLine($"{AnyName(any[0])}"); } // => RuntimeBinderException
catch (Exception e) { Console.Error.WriteLine(e); } // cannot apply indexing []
}
//Animal names: Bear-1, Cat-2a
//Bear-3, Dog-4, Cat-5
} //end of class AssignDerivedClass
저는 개인적으로 수업의 확장자를 가진 lib를 만드는 것을 좋아합니다.
public static List<TTo> Cast<TFrom, TTo>(List<TFrom> fromlist)
where TFrom : class
where TTo : class
{
return fromlist.ConvertAll(x => x as TTo);
}
왜냐하면 C#에서는 현재 그러한 종류의 상속 변환을 허용하지 않기 때문입니다.
이것은 BigJim의 훌륭한 답변의 연장선이다.
에는 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.NodeBase
with가 Children
아이들에게서 일반적으로 O(1) 검색을 할 수 있는 방법이 필요했어요.Children
그래서 비싼 복사나 반복은 피하고 싶었습니다. 저는 빅짐의 해서 '빅짐'을 캐스팅했습니다.Dictionary<whatever specific type>
인 「」에 Dictionary<NodeBase>
:
// Abstract parent class
public abstract class NodeBase
{
public abstract IDictionary<string, NodeBase> Children { get; }
...
}
// Implementing child class
public class RealNode : NodeBase
{
private Dictionary<string, RealNode> containedNodes;
public override IDictionary<string, NodeBase> Children
{
// Using a modification of Bigjim's code to cast the Dictionary:
return new IDictionary<string, NodeBase>().CastDictionary<string, RealNode, NodeBase>();
}
...
}
이거 잘 됐다.결국 는 관련 없는 인 것을 .FindChild()
기본 클래스의 메서드를 대신 검색합니다.간단한 것으로 할 수 .IEnumerable
(이러한)
이런 (특히 하는 문제인가)이 있을..Cast<>
★★★★★★★★★★★★★★★★★」.ConvertAll<>
는 다음과 같습니다
컬렉션 전체를 캐스팅해야 합니까?아니면 추상적인 방법을 사용하여 작업을 수행하는 데 필요한 전문 지식을 보유하여 컬렉션에 직접 액세스하지 않아도 됩니까?
때로는 가장 간단한 해결책이 최선입니다.
이 경우에도 하실 수 있습니다.System.Runtime.CompilerServices.Unsafe
NuGet에 참조를 작성하기 위한 .List
:
using System.Runtime.CompilerServices;
...
class Tool { }
class Hammer : Tool { }
...
var hammers = new List<Hammer>();
...
var tools = Unsafe.As<List<Tool>>(hammers);
위의 예에서 기존 예에 액세스할 수 있습니다.Hammer
목록 내의 인스턴스:tools
변수.추가 중Tool
목록에 있는 인스턴스는ArrayTypeMismatchException
예외적인 이유는tools
와 같은 변수를 참조합니다.hammers
.
이 글을 다 읽었는데, 모순으로 보이는 게 뭔지 지적하고 싶어서요.
컴파일러는 Lists:를 사용하여 할당을 수행할 수 없도록 합니다.
List<Tiger> myTigersList = new List<Tiger>() { new Tiger(), new Tiger(), new Tiger() };
List<Animal> myAnimalsList = myTigersList; // Compiler error
그러나 컴파일러는 어레이에 전혀 문제가 없습니다.
Tiger[] myTigersArray = new Tiger[3] { new Tiger(), new Tiger(), new Tiger() };
Animal[] myAnimalsArray = myTigersArray; // No problem
그 임무가 안전한지 아닌지에 대한 논쟁은 여기서 그치지 않는다.어레이로 한 할당은 안전하지 않습니다.그걸 증명하기 위해, 내가 이걸 따라간다면:
myAnimalsArray[1] = new Giraffe();
런타임 예외 "ArrayTypeMismatchException"이 표시된다.이걸 어떻게 설명할까?컴파일러가 정말 제가 바보 같은 일을 하는 것을 방지하고 싶다면 어레이 할당을 할 수 없도록 해야 합니다.
늦을지도 몰라.
어레이로 변환하는 것도 가능합니다.
main()
{
List<Camel> camels = new List<Camel>();
Reproducton(camels.ToArray());
}
public void Reproducton(Animal[] animals)
{
foreach(var animal in animals.ToList())
{
var baby = animal.Reproduce();
}
}
언급URL : https://stackoverflow.com/questions/1817300/convert-listderivedclass-to-listbaseclass
'programing' 카테고리의 다른 글
Swift's Codable을 사용하여 사전에 인코딩하려면 어떻게 해야 합니까? (0) | 2023.04.21 |
---|---|
C++ 문자열에서 마지막 문자 제거 (0) | 2023.04.21 |
iPhone SDK: loadView와 viewDidLoad의 차이점은 무엇입니까? (0) | 2023.04.21 |
테이블에 트리거가 있는 경우 OUTPUT 절과 함께 UPDATE를 사용할 수 없습니다. (0) | 2023.04.21 |
C#의 Excel 셀에 새 행을 프로그래밍 방식으로 삽입하려면 어떻게 해야 합니까? (0) | 2023.04.21 |