programing

목록을 목록으로 변환

muds 2023. 4. 21. 21:33
반응형

목록을 목록으로 변환

클래스할 수 만, 「」/「」를 수 입니까?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#의 가능성이 있습니다.

  1. IReadOnlyList,IEnumerable => 오류는 없고, 활자는 안전합니다.아마 필요한 것.
  2. List<> 방법 => free / 방방 = > 오 types / typesafe 아 the
  3. Array / errors => "실행시 오류 발생" / "실행시 오류
  4. 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

반응형