상세 컨텐츠

본문 제목

foreach를 사용할 수 있는 일반화 클래스

C#

by 메타 스토리 2023. 9. 5. 10:01

본문

foreach 루프를 사용할 수 있는 일반화(generic) 클래스는 IEnumerable<T> 인터페이스를 구현한 클래스입니다. 이 인터페이스를 구현하면 해당 클래스의 인스턴스를 foreach 루프에서 반복할 수 있게 됩니다.

IEnumerable<T> 인터페이스는 C#에서 컬렉션을 반복하는 데 사용되는 기본적인 인터페이스 중 하나입니다. 이 인터페이스는 다음과 같이 정의되어 있습니다:

public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}
여기서 중요한 것은 GetEnumerator 메서드입니다. 이 메서드는 컬렉션에서 IEnumerator<T> 인터페이스를 반환합니다. IEnumerator<T> 인터페이스를 구현한 클래스는 컬렉션을 반복하는 데 필요한 메서드 및 속성을 제공합니다.

IEnumerator<T> 인터페이스는 다음과 같이 정의되어 있습니다:

public interface IEnumerator<out T> : IDisposable, IEnumerator
{
    T Current { get; }
    bool MoveNext();
    void Reset();
}
IEnumerable<T> 인터페이스를 구현하는 클래스는 GetEnumerator 메서드를 구현하고, 이 메서드에서 해당 클래스의 반복자(enumerator)를 반환해야 합니다. 반복자는 IEnumerator<T> 인터페이스를 구현하며, Current 속성을 통해 현재 요소에 접근하고, MoveNext 메서드를 호출하여 다음 요소로 이동합니다.

일반적으로 foreach 루프를 사용하려면 컬렉션 클래스가 IEnumerable<T> 인터페이스를 구현하고 있어야 합니다. 이를 통해 foreach 루프에서 컬렉션의 각 요소를 순회할 수 있게 됩니다.

IEnumerable<T> 인터페이스를 구현한 컬렉션들은 C#에서 반복 가능한 컬렉션을 나타냅니다. 이 인터페이스를 구현하면 컬렉션의 요소를 순회할 수 있는 기능을 제공합니다. 아래는 몇 가지 IEnumerable<T> 인터페이스를 구현한 일반적인 컬렉션의 몇 가지 예제입니다:

List<T>: List<T>는 가장 많이 사용되는 IEnumerable<T>를 구현한 컬렉션 중 하나입니다. 리스트에 요소를 추가하고 순회하는 데 사용할 수 있습니다.
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

Array: 배열도 IEnumerable<T>를 구현합니다. 배열은 고정된 크기를 가지며 요소를 순회할 수 있습니다.
int[] array = new int[] { 1, 2, 3, 4, 5 };

Dictionary<TKey, TValue>: 사전은 IEnumerable<KeyValuePair<TKey, TValue>>를 구현합니다. foreach를 사용하여 키-값 쌍을 순회할 수 있습니다.
Dictionary<string, int> dictionary = new Dictionary<string, int>();
dictionary.Add("One", 1);
dictionary.Add("Two", 2);
dictionary.Add("Three", 3);

HashSet<T>: HashSet<T>는 고유한 값만 저장하는 집합을 나타내며 IEnumerable<T>를 구현합니다.
HashSet<string> set = new HashSet<string> { "apple", "banana", "cherry" };

LinkedList<T>: LinkedList<T>는 노드 기반 연결 리스트를 나타내며 IEnumerable<T>를 구현합니다.
LinkedList<int> linkedList = new LinkedList<int>();
linkedList.AddLast(1);
linkedList.AddLast(2);
linkedList.AddLast(3);

이러한 컬렉션들은 IEnumerable<T>를 구현하여 foreach 루프를 사용하여 요소를 순회할 수 있도록 합니다. 이것은 C#에서 컬렉션을 다루는 일반적인 방법 중 하나입니다.

 

 

// 1. IEnumerable<Person>을 상속받는 방식

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
    }
}

public class PeopleCollection : IEnumerable<Person>
{
    private List<Person> people = new List<Person>();

    public void Add(Person person)
    {
        people.Add(person);
    }

    public void Edit(string name, int age)
    {
        Person person = people.FirstOrDefault(p => p.Name == name);
        if (person != null)
        {
            person.Age = age;
        }
    }

    public void Remove(string name)
    {
        Person personToRemove = people.FirstOrDefault(p => p.Name == name);
        if (personToRemove != null)
        {
            people.Remove(personToRemove);
        }
    }

    public IEnumerator<Person> GetEnumerator()
    {
        return people.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

class Program
{
    static void Main()
    {
        PeopleCollection peopleCollection = new PeopleCollection();

        Person person1 = new Person { Name = "Alice", Age = 30 };
        Person person2 = new Person { Name = "Bob", Age = 25 };
        Person person3 = new Person { Name = "Charlie", Age = 35 };

        peopleCollection.Add(person1);
        peopleCollection.Add(person2);
        peopleCollection.Add(person3);

        foreach (var person in peopleCollection)
        {
            person.SayHello();
        }

        Console.WriteLine("\nAfter editing Alice's age:");
        peopleCollection.Edit("Alice", 31);

        foreach (var person in peopleCollection)
        {
            person.SayHello();
        }

        Console.WriteLine("\nAfter removing Bob:");
        peopleCollection.Remove("Bob");

        foreach (var person in peopleCollection)
        {
            person.SayHello();
        }
    }
}

Hello, my name is Alice and I am 30 years old.
Hello, my name is Bob and I am 25 years old.
Hello, my name is Charlie and I am 35 years old.

After editing Alice's age:
Hello, my name is Alice and I am 31 years old.
Hello, my name is Bob and I am 25 years old.
Hello, my name is Charlie and I am 35 years old.

After removing Bob:
Hello, my name is Alice and I am 31 years old.
Hello, my name is Charlie and I am 35 years old.

 

 

 

 

 

 

// 2, List<Person> 상속받는 방식.

using System;
using System.Collections.Generic;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
    }
}

public class PeopleCollection : List<Person>
{
    public void AddPerson(string name, int age)
    {
        Add(new Person { Name = name, Age = age });
    }

    public void EditPerson(string name, int age)
    {
        foreach (var person in this)
        {
            if (person.Name == name)
            {
                person.Age = age;
                return;
            }
        }
    }

    public void RemovePerson(string name)
    {
        RemoveAll(person => person.Name == name);
    }

    public void DisplayPeople()
    {
        Console.WriteLine("Current People:");
        foreach (var person in this)
        {
            Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
        }
    }
}

class Program
{
    static void Main()
    {
        PeopleCollection peopleCollection = new PeopleCollection();

        peopleCollection.AddPerson("Alice", 30);
        peopleCollection.AddPerson("Bob", 25);

        Console.WriteLine("Initial List:");
        peopleCollection.DisplayPeople();

        Console.WriteLine("\nAfter Editing:");
        peopleCollection.EditPerson("Alice", 35);
        peopleCollection.DisplayPeople();

        Console.WriteLine("\nAfter Removing:");
        peopleCollection.RemovePerson("Alice");
        peopleCollection.DisplayPeople();
        
        Console.WriteLine("\nAfter Calling SayHello:");
        foreach (var person in peopleCollection)
        {
            person.SayHello();
        }
    }
}

 

Initial List:
Current People:
Name: Alice, Age: 30
Name: Bob, Age: 25

After Editing:
Current People:
Name: Alice, Age: 35
Name: Bob, Age: 25

After Removing:
Current People:
Name: Bob, Age: 25

After Calling SayHello:
Hello, my name is Bob and I am 25 years old.

 

 

 

//PeopleCollectionList<Person>을 상속하고 Person 객체를 List에 추가하여 구현할 수 있습니다. 다음은 이러한 방식으로 구현한 예제입니다:

using System;
using System.Collections.Generic;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
    }
}

public class PeopleCollection : List<Person>
{
    public void AddPerson(string name, int age)
    {
        Person person = new Person { Name = name, Age = age };
        this.Add(person);
    }

    public void EditPersonAge(string name, int newAge)
    {
        Person person = this.Find(p => p.Name == name);
        if (person != null)
        {
            person.Age = newAge;
        }
    }

    public void RemovePerson(string name)
    {
        Person person = this.Find(p => p.Name == name);
        if (person != null)
        {
            this.Remove(person);
        }
    }
}

class Program
{
    static void Main()
    {
        PeopleCollection people = new PeopleCollection();

        people.AddPerson("Alice", 30);
        people.AddPerson("Bob", 25);
        people.AddPerson("Charlie", 35);

        foreach (var person in people)
        {
            person.SayHello();
        }

        people.EditPersonAge("Alice", 31);

        Console.WriteLine("\nAfter editing Alice's age:");
        foreach (var person in people)
        {
            person.SayHello();
        }

        people.RemovePerson("Bob");

        Console.WriteLine("\nAfter removing Bob:");
        foreach (var person in people)
        {
            person.SayHello();
        }
    }
}

Hello, my name is Alice and I am 30 years old.
Hello, my name is Bob and I am 25 years old.
Hello, my name is Charlie and I am 35 years old.

After editing Alice's age:
Hello, my name is Alice and I am 31 years old.
Hello, my name is Bob and I am 25 years old.
Hello, my name is Charlie and I am 35 years old.

After removing Bob:
Hello, my name is Alice and I am 31 years old.
Hello, my name is Charlie and I am 35 years old.

 

 

 

List<Person>을 상속받는 방식과 IEnumerable<Person> 인터페이스를 구현하는 방식은 각각의 장단점이 있습니다. 어떤 방식을 선택할지는 상황과 요구사항에 따라 다를 수 있습니다.

List<Person> 상속 방식:

장점:
기본적인 리스트 동작을 모두 상속받으므로 리스트를 사용하는 다양한 메서드와 기능을 활용할 수 있습니다. 예를 들어 Add, Remove, Clear, Sort 등의 메서드를 쉽게 사용할 수 있습니다.
코드가 간결하며 직관적입니다.
단점:
List<Person> 클래스를 직접 상속받기 때문에 다중 상속이 필요한 경우에는 제한이 있을 수 있습니다.
클래스의 구조를 변경하기 위해 해당 클래스의 소스 코드에 직접 접근해야 할 수 있으므로 라이브러리 클래스일 경우 수정이 어려울 수 있습니다.
IEnumerable<Person> 인터페이스 구현 방식:

장점:
인터페이스를 구현함으로써 클래스가 다른 클래스와 호환성을 가질 수 있습니다. 이 클래스를 다른 컬렉션과 함께 사용할 때 더 유연성을 제공합니다.
클래스의 동작을 더 정확하게 제어할 수 있으며, 필요한 메서드와 속성만 노출시킬 수 있습니다.
단점:
컬렉션의 기본 동작을 사용하기 위해 별도의 구현이 필요하며, 코드가 상대적으로 더 복잡해질 수 있습니다.
필요한 경우 컬렉션 관련 메서드와 동작을 직접 구현해야 하므로 시간과 노력이 더 필요합니다.
따라서 어떤 방식을 선택할지는 프로젝트의 특정 요구사항과 목표에 따라 다릅니다. 간단한 리스트를 다루는 경우에는 List<Person>을 상속받는 방식이 편리할 수 있지만, 라이브러리나 클래스의 재사용성을 고려하거나 더 정교한 컨트롤이 필요한 경우에는 IEnumerable<Person> 인터페이스 구현 방식이 유용할 수 있습니다.

'C#' 카테고리의 다른 글

LinkedList<T>  (0) 2023.09.05
HashSet<T>  (0) 2023.09.05
Dictionary  (0) 2023.09.05
Stack  (0) 2023.09.05
Queue  (0) 2023.09.05

관련글 더보기