달력

2

« 2025/2 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
2016. 8. 4. 18:34

인터페이스, 이벤트, 인덱서 프로그래밍/C#2016. 8. 4. 18:34

https://msdn.microsoft.com/ko-kr/library/ms173156.aspx

인터페이스


1. 인터페이스는 클래스 또는 구조체가 구현할 수 있는 연관 기능의 집합에 대한 정의를 포함합니다.

2. 예를들어 인터페이스를 사용함으로서 한 클래스에서 여러 소스의 동작을 포함할 수 있습니다.

3. C# 언어는 클래스에 대한 다중 상속을 지원하지 않기 때문에, 해당 기능이 중요합니다.

4. 또한 구조체는 다른 구조체나 클래스에서 실제로 상속할 수 없기 때문에 구조체에 대한 상속을 시뮬레이트하려는 경우 인터페이스를 사용해야 합니다.

5. 다음 예제에서는 인터페이스 키워드를 사용하여 인터페이스를 정의하는 방법을 보여줍니다.


interface IEquatable<T>
{
    bool Equals(T obj);
}


6. IEquatable<T> 인터페이스를 구현하는 모든 클래스 또는 구조체는, 인터페이스가 지정하는 기호와 일치하는 Equals 메서드에 대한 정의를 포함해야 합니다.

7. 따라서 IEquatable<T>을 구현하는 클래스를 계산하여 클래스의 인스턴스에서 동일한 클래스의 다른 인스턴스와 동일한지 여부를 확인할 수 있는 Equals 메서드를 포함할 수 있습니다.

8. IEquatable<T>의 정의에서는 Equals에 대한 구현을 제공하지 않습니다.

9. 인터페이스는 서명만 정의합니다.

10. 이런 방식으로 C#의 인터페이스는 모든 메서드가 추상인 추상 클래스와 유사합니다.

11. 그러나 클래스 또는 구조체는 여러 인터페이스를 구현할 수 있지만 클래스는 추상인지 여부에 관계없이 단일 클래스만 상속할 수 있습니다.

12. 따라서 인터페이스를 사용하여 여러 소스의 동작을 클래스에 포함할 수 있습니다.

13. 추상 클래스에 대한 자세한 내용은 추상 및 봉인 클래스와 클래스 멤버를 참조하십시오.

14. 인터페이스에는 메서드, 속성, 이벤트, 인덱서 또는 이러한 네 가지 멤버 형식의 조합이 포함될 수 있습니다.

15. 예제에 대한 링크는 관련 단원을 참조하세요.

16. 인터페이스에는 상수, 필드, 연산자, 인스턴스 생성자, 소멸자 또는 형식이 포함될 수 없습니다.

17. 인터페이스 멤버는 자동으로 public이 되며 액세스 한정자를 포함할 수 없습니다.

18. 또한 멤버를 정적으로도 선언할 수 없습니다.

19. 인터페이스 멤버를 구현하려면 구현 클래스의 해당 멤버가 공용이고 비정적이어야 하며 인터페이스 멤버와 동일한 이름 및 서명을 사용해야 합니다.

20. 클래스 또는 구조체에서 인터페이스를 구현하는 경우 인터페이스에서 정의하는 모든 멤버에 대해 구현을 제공해야 합니다.

21. 인터페이스 자체는 클래스 또는 구조체에서 기본 클래스 기능을 상속하는 방식으로 상속할 수 있는 기능을 제공하지 않습니다.

22. 그러나 기본 클래스에서 인터페이스를 구현하는 경우에는 기본 클래스에서 파생되는 모든 클래스가 해당 구현을 상속합니다.

23. 다음 예제는 IEquatable<T> 인터페이스의 한 가지 구현을 보여줍니다.

24. 구현하려는 Car 클래스는 Equals 메서드의 구현을 제공해야 합니다.


public class Car : IEquatable<Car>
{
    public string Make {get; set;}
    public string Model { get; set; }
    public string Year { get; set; }

    // Implementation of IEquatable<T> interface
    public bool Equals(Car car)
    {
        if (this.Make == car.Make &&
            this.Model == car.Model &&
            this.Year == car.Year)
        {
            return true;
        }
        else
            return false;
    }
}


25. 클래스의 속성 및 인덱서는 인터페이스에 정의된 속성이나 인덱서에 대해 추가 접근자를 정의할 수 있습니다.

26. 예를 들어 인터페이스는 get 접근자가 있는 속성을 선언할 수 있습니다.

27. 인터페이스를 구현하는 클래스는 get 및 set 접근자를 둘 다 사용하는 동일한 속성을 선언할 수 있습니다.

28. 그러나 속성 또는 인덱서에서 명시적 구현을 사용하는 경우에는 접근자가 일치해야 합니다.

29. 명시적 구현에 대한 자세한 내용은 명시적 인터페이스 구현(C# 프로그래밍 가이드) 및 인터페이스 속성(C# 프로그래밍 가이드)을 참조하세요.

30. 인터페이스는 다른 인터페이스를 구현할 수 있습니다.

31. 클래스는 상속하는 기본 클래스 또는 다른 인터페이스에서 구현하는 인터페이스를 통해 인터페이스를 여러 번 포함할 수 있습니다.

32. 그러나 클래스는 인터페이스의 구현을 한 번만 제공할 수 있으며 클래스가 인터페이스를 클래스 정의의 일부로 선언하는 경우에만 제공할 수 있습니다(class ClassName : InterfaceName).

33. 인터페이스를 구현하는 기본 클래스를 상속했기 때문에 인터페이스가 상속되는 경우 기본 클래스는 인터페이스 멤버의 구현을 제공합니다.

34. 그러나 파생 클래스는 상속된 구현을 사용하는 대신 인터페이스 멤버를 다시 구현할 수 있습니다.

35. 또한 기본 클래스는 가상 멤버를 사용하여 인터페이스 멤버를 구현할 수 있습니다.

36.  이 경우 파생 클래스는 가상 멤버를 재정의하여 인터페이스 동작을 변경할 수 있습니다.

37. 가상 멤버에 대한 자세한 내용은 다형성을 참조하세요.


인터페이스 요약

인터페이스에는 다음과 같은 속성이 있습니다.

1. 인터페이스는 추상 기본 클래스와 같습니다. 인터페이스를 구현하는 모든 클래스 또는 구조체는 모든 멤버를 구현해야 합니다.

2. 인터페이스는 직접 인스턴스화할 수 없습니다. 해당 멤버는 인터페이스를 구현하는 클래스 또는 구조체에 의해 구현됩니다.

3. 인터페이스는 이벤트, 인덱서, 메서드 및 속성을 포함할 수 있습니다.

4. 인터페이스에는 메서드의 구현이 포함되지 않습니다.

5. 클래스 또는 구조체는 여러 인터페이스를 구현할 수 있습니다. 클래스는 기본 클래스를 상속할 수 있으며 하나 이상의 인터페이스를 제공할 수도 있습니다.




명시적 인터페이스 구현


1. 클래스에서 시그니처가 동일한 멤버가 들어 있는 두 인터페이스를 구현하는 경우 클래스에서 이 멤버를 구현하면 두 인터페이스 모두에서 이 멤버를 해당 구현으로 사용하게 됩니다.

2. 다음 예제에서 모든 호출을 Paint 같은 메서드를 호출 합니다.


class Test 
{
    static void Main()
    {
        SampleClass sc = new SampleClass();
        IControl ctrl = (IControl)sc;
        ISurface srfc = (ISurface)sc;

        // The following lines all call the same method.
        sc.Paint();
        ctrl.Paint();
        srfc.Paint();
    }
}


interface IControl
{
    void Paint();
}
interface ISurface
{
    void Paint();
}
class SampleClass : IControl, ISurface
{
    // Both ISurface.Paint and IControl.Paint call this method. 
    public void Paint()
    {
        Console.WriteLine("Paint method in SampleClass");
    }
}

// Output:
// Paint method in SampleClass
// Paint method in SampleClass
// Paint method in SampleClass


3. 그러나 두 인터페이스 멤버가 동일한 기능을 수행하지 않는 경우에는 인터페이스 하나 또는 모두의 구현이 잘못될 수 있습니다.

4. 인터페이스를 통해서만 호출되고 특정 인터페이스에만 관련되는 클래스 멤버를 만드는 방식으로 인터페이스 멤버를 명시적으로 구현할 수 있습니다.

5. 이렇게 하려면 인터페이스 이름과 마침표를 사용하여 클래스 멤버의 이름을 지정해야 합니다. 예를 들면 다음과 같습니다.


public class SampleClass : IControl, ISurface
{
    void IControl.Paint()
    {
        System.Console.WriteLine("IControl.Paint");
    }
    void ISurface.Paint()
    {
        System.Console.WriteLine("ISurface.Paint");
    }
}


6. 클래스 멤버 IControl.Paint는 IControl 인터페이스를 통해서만 사용할 수 있고 ISurface.Paint는 ISurface를 통해서만 사용할 수 있습니다.

7. 두 메서드 구현은 서로 별개이며 어느 쪽도 클래스에 대해 직접 사용할 수 없습니다. 예를 들면 다음과 같습니다.


// Call the Paint methods from Main.

SampleClass obj = new SampleClass();
//obj.Paint();  // Compiler error.

IControl c = (IControl)obj;
c.Paint();  // Calls IControl.Paint on SampleClass.

ISurface s = (ISurface)obj;
s.Paint(); // Calls ISurface.Paint on SampleClass.

// Output:
// IControl.Paint
// ISurface.Paint


8. 두 인터페이스에서 각각 이름이 동일하지만 서로 다른 속성 및 메서드 등의 멤버를 선언할 때 발생하는 문제의 해결에도 명시적 구현이 사용됩니다.


interface ILeft
{
    int P { get;}
}
interface IRight
{
    int P();
}


9. 두 인터페이스를 모두 구현하려는 경우에는 클래스에서 P 속성이나 P 메서드 또는 둘 모두에 대해 명시적 구현을 사용해야 컴파일러 오류를 방지할 수 있습니다.

10. 예를 들면 다음과 같습니다.


class Middle : ILeft, IRight
{
    public int P() { return 0; }
    int ILeft.P { get { return 0; } }
}




방법: 인터페이스 멤버를 명시적으로 구현


1. 다음 예제에서는 IDimensions인터페이스를 선언한 다음 인터페이스 멤버인 getLength와 getWidth를 명시적으로 구현하는 Box 클래스를 선언합니다.

2. 멤버는 인터페이스 인스턴스 dimensions을 통해 액세스할 수 있습니다.


interface IDimensions
{
    float getLength();
    float getWidth();
}

class Box : IDimensions
{
    float lengthInches;
    float widthInches;

    Box(float length, float width)
    {
        lengthInches = length;
        widthInches = width;
    }
    // Explicit interface member implementation: 
    float IDimensions.getLength()
    {
        return lengthInches;
    }
    // Explicit interface member implementation:
    float IDimensions.getWidth()
    {
        return widthInches;
    }

    static void Main()
    {
        // Declare a class instance box1:
        Box box1 = new Box(30.0f, 20.0f);

        // Declare an interface instance dimensions:
        IDimensions dimensions = (IDimensions)box1;

        // The following commented lines would produce compilation 
        // errors because they try to access an explicitly implemented
        // interface member from a class instance:                   
        //System.Console.WriteLine("Length: {0}", box1.getLength());
        //System.Console.WriteLine("Width: {0}", box1.getWidth());

        // Print out the dimensions of the box by calling the methods 
        // from an instance of the interface:
        System.Console.WriteLine("Length: {0}", dimensions.getLength());
        System.Console.WriteLine("Width: {0}", dimensions.getWidth());
    }
}
/* Output:
    Length: 30
    Width: 20
*/


강력한 프로그래밍


1. Main 메서드에서 다음 줄은 컴파일 오류를 발생시키므로 주석 처리합니다.

2. 명시적으로 구현된 인터페이스 멤버는 클래스 인스턴스에서 액세스할 수 없습니다.


//System.Console.WriteLine("Length: {0}", box1.getLength());
//System.Console.WriteLine("Width: {0}", box1.getWidth());


3. 또한, 메서드는 인터페이스의 인스턴스에서 호출되므로 Main 메서드에서 다음 줄은 상자의 크기를 성공적으로 출력합니다.


System.Console.WriteLine("Length: {0}", dimensions.getLength());
System.Console.WriteLine("Width: {0}", dimensions.getWidth());




방법: 두 인터페이스의 멤버를 명시적으로 구현


1. 명시적으로 인터페이스를 구현하면 멤버 이름이 동일한 두 개의 인터페이스를 구현하여 각 인터페이스 멤버에 별도의 구현을 제공할 수 있습니다.

2. 다음 예제에서는 상자의 크기를 미터와 인치 단위로 표시합니다.

3. Box 클래스는 서로 다른 단위를 나타내는 두 인터페이스인 IEnglishDimensions 및 IMetricDimensions를 구현합니다.

4. 두 인터페이스에는 모두 동일한 멤버 이름 Length와 Width가 있습니다.


// Declare the English units interface:
interface IEnglishDimensions
{
    float Length();
    float Width();
}

// Declare the metric units interface:
interface IMetricDimensions
{
    float Length();
    float Width();
}

// Declare the Box class that implements the two interfaces:
// IEnglishDimensions and IMetricDimensions:
class Box : IEnglishDimensions, IMetricDimensions
{
    float lengthInches;
    float widthInches;

    public Box(float length, float width)
    {
        lengthInches = length;
        widthInches = width;
    }

    // Explicitly implement the members of IEnglishDimensions:
    float IEnglishDimensions.Length()
    {
        return lengthInches;
    }

    float IEnglishDimensions.Width()
    {
        return widthInches;
    }

    // Explicitly implement the members of IMetricDimensions:
    float IMetricDimensions.Length()
    {
        return lengthInches * 2.54f;
    }

    float IMetricDimensions.Width()
    {
        return widthInches * 2.54f;
    }

    static void Main()
    {
        // Declare a class instance box1:
        Box box1 = new Box(30.0f, 20.0f);

        // Declare an instance of the English units interface:
        IEnglishDimensions eDimensions = (IEnglishDimensions)box1;

        // Declare an instance of the metric units interface:
        IMetricDimensions mDimensions = (IMetricDimensions)box1;

        // Print dimensions in English units:
        System.Console.WriteLine("Length(in): {0}", eDimensions.Length());
        System.Console.WriteLine("Width (in): {0}", eDimensions.Width());

        // Print dimensions in metric units:
        System.Console.WriteLine("Length(cm): {0}", mDimensions.Length());
        System.Console.WriteLine("Width (cm): {0}", mDimensions.Width());
    }
}
/* Output:
    Length(in): 30
    Width (in): 20
    Length(cm): 76.2
    Width (cm): 50.8
*/


강력한 프로그래밍


1. 인치를 기본 단위로 하려면 메서드 Length와 Width를 정상적으로 구현하고 IMetricDimensions 인터페이스에서 Length와 Width 메서드를 명시적으로 구현해야 합니다.


// Normal implementation:
public float Length()
{
    return lengthInches;
}
public float Width()
{
    return widthInches;
}

// Explicit implementation:
float IMetricDimensions.Length()
{
    return lengthInches * 2.54f;
}
float IMetricDimensions.Width()
{
    return widthInches * 2.54f;
}


2. 이런 경우 인치 단위는 클래스 인스턴스에서 액세스하고 미터 단위는 인터페이스 인스턴스에서 액세스할 수 있습니다.


public static void Test()
{
    Box box1 = new Box(30.0f, 20.0f);
    IMetricDimensions mDimensions = (IMetricDimensions)box1;

    System.Console.WriteLine("Length(in): {0}", box1.Length());
    System.Console.WriteLine("Width (in): {0}", box1.Width());
    System.Console.WriteLine("Length(cm): {0}", mDimensions.Length());
    System.Console.WriteLine("Width (cm): {0}", mDimensions.Width());
}




인터페이스 속성

https://msdn.microsoft.com/ko-kr/library/64syzecx.aspx


1. interface(C# 참조)에서 속성을 선언할 수 있습니다.

2. 다음은 인터페이스 인덱서 접근자의 예제입니다.

3. 인터페이스 속성의 접근자에는 본문이 없습니다.

4. 접근자를 사용하는 목적은 속성이 읽기/쓰기, 읽기 전용 또는 쓰기 전용인지 여부를 나타내는 것입니다.

public interface ISampleInterface
{
    // Property declaration:
    string Name
    {
        get;
        set;
    }
}


예제

1. 다음 예제의 IEmployee 인터페이스에는 읽기/쓰기 속성인 Name과 읽기 전용 속성인 Counter가 있습니다.

2. Employee 클래스는 IEmployee 인터페이스를 구현하고 이 두 속성을 사용합니다.

3. 프로그램에서는 새 직원 이름과 현재 번호를 읽어 들이고 직원 이름과 계산된 직원 번호를 표시합니다.

4. 멤버가 선언된 인터페이스를 참조하는 속성의 정규화된 이름을 사용할 수 있습니다.

string IEmployee.Name
{
    get { return "Employee Name"; }
    set { }
}


5. 이를 명시적 인터페이스 구현이라고 합니다.

6. 예를 들어, Employee 클래스가ICitizen 및 IEmployee라는 두 개의 인터페이스를 구현하며 두 인터페이스 모두에 Name 속성이 있는 경우 해당 인터페이스 멤버를 명시적으로 구현해야 합니다.

string IEmployee.Name
{
    get { return "Employee Name"; }
    set { }
}


string ICitizen.Name
{
    get { return "Citizen Name"; }
    set { }
}


interface IEmployee
{
    string Name
    {
        get;
        set;
    }

    int Counter
    {
        get;
    }
}

public class Employee : IEmployee
{
    public static int numberOfEmployees;

    private string name;
    public string Name  // read-write instance property
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }

    private int counter;
    public int Counter  // read-only instance property
    {
        get
        {
            return counter;
        }
    }

    public Employee()  // constructor
    {
        counter = ++counter + numberOfEmployees;
    }
}

class TestEmployee
{
    static void Main()
    {
        System.Console.Write("Enter number of employees: ");
        Employee.numberOfEmployees = int.Parse(System.Console.ReadLine());

        Employee e1 = new Employee();
        System.Console.Write("Enter the name of the new employee: ");
        e1.Name = System.Console.ReadLine();

        System.Console.WriteLine("The employee information:");
        System.Console.WriteLine("Employee number: {0}", e1.Counter);
        System.Console.WriteLine("Employee name: {0}", e1.Name);
    }
}


            210

Hazem Abolrous


샘플 출력

Enter number of employees: 210

Enter the name of the new employee: Hazem Abolrous

The employee information:

Employee number: 211

Employee name: Hazem Abolrous




인터페이스의 인덱서

https://msdn.microsoft.com/ko-kr/library/tkyhsw31.aspx


1. interface(C# 참조)에서 인덱서를 선언할 수 있습니다.

2. 인터페이스 인덱서의 접근자와 클래스 인덱서의 접근자는 다음과 같은 차이점이 있습니다.

1) 인터페이스 접근자는 한정자를 사용하지 않습니다.

2) 인터페이스 접근자에는 본문이 없습니다.

3. 따라서 접근자의 목적은 인덱서가 읽기/쓰기, 읽기 전용 또는 쓰기 전용인지 여부를 나타내는 것입니다.

4. 다음은 인터페이스 인덱서 접근자의 예제입니다.


public interface ISomeInterface
{
    //...

    // Indexer declaration:
    string this[int index]
    {
        get;
        set;
    }
}


5. 인덱서의 시그니처는 같은 인터페이스에 선언된 다른 모든 인덱서의 시그니처와 달라야 합니다.


예제

1. 다음 예제는 인터페이스 인덱서의 구현 방법을 보여 줍니다.


// Indexer on an interface:
public interface ISomeInterface
{
    // Indexer declaration:
    int this[int index]
    {
        get;
        set;
    }
}

// Implementing the interface.
class IndexerClass : ISomeInterface
{
    private int[] arr = new int[100];
    public int this[int index]   // indexer declaration
    {
        get
        {
            // The arr object will throw IndexOutOfRange exception.
            return arr[index];
        }
        set
        {
            arr[index] = value;
        }
    }
}

class MainClass
{
    static void Main()
    {
        IndexerClass test = new IndexerClass();
        System.Random rand = new System.Random();
        // Call the indexer to initialize its elements.
        for (int i = 0; i < 10; i++)
        {
            test[i] = rand.Next();
        }
        for (int i = 0; i < 10; i++)
        {
            System.Console.WriteLine("Element #{0} = {1}", i, test[i]);
        }

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }
}
/* Sample output:
    Element #0 = 360877544
    Element #1 = 327058047
    Element #2 = 1913480832
    Element #3 = 1519039937
    Element #4 = 601472233
    Element #5 = 323352310
    Element #6 = 1422639981
    Element #7 = 1797892494
    Element #8 = 875761049
    Element #9 = 393083859
 */


2. 위 예제에서 인터페이스 멤버의 정규화된 이름을 사용하여 명시적 인터페이스 멤버를 구현할 수 있습니다.

3. 예를 들면 다음과 같습니다.


public string ISomeInterface.this 


4. 그러나 정규화된 이름은 클래스에서 인덱서 시그니처가 같은 두 개 이상의 인터페이스를 구현하는 경우에 모호성을 피하고자 할 때 필요합니다.

5. 예를 들어, Employee 클래스가 ICitizen 및 IEmployee라는 두 개의 인터페이스를 구현하고 두 인터페이스에 동일한 인덱서 시그니처가 있는 경우 해당 인터페이스 멤버를 명시적으로 구현해야 합니다.

6. 예를 들면 다음과 같습니다.


public string IEmployee.this 


7. 위의 선언은 IEmployee 인터페이스에서 인덱서를 구현합니다.


public string ICitizen.this 


8. 그러나 위의 선언은 ICitizen 인터페이스에서 인덱서를 구현합니다.




방법: 인터페이스 이벤트 구현

https://msdn.microsoft.com/ko-kr/library/ak9w5846.aspx



인터페이스에서는 이벤트를 선언할 수 있습니다. 다음 예제에서는 클래스에 인터페이스 이벤트를 구현하는 방법을 보여 줍니다. 기본적으로 규칙은 인터페이스 메서드나 속성을 구현할 때와 동일합니다.

클래스에 인터페이스 이벤트를 구현하려면

  • 클래스에 이벤트를 선언하고 이를 적절한 영역에서 호출합니다.

    namespace ImplementInterfaceEvents
    {
        public interface IDrawingObject
        {
            event EventHandler ShapeChanged;
        }
        public class MyEventArgs : EventArgs 
        {
            // class members
        }
        public class Shape : IDrawingObject
        {
            public event EventHandler ShapeChanged;
            void ChangeShape()
            {
                // Do something here before the event…
    
                OnShapeChanged(new MyEventArgs(/*arguments*/));
    
                // or do something here after the event. 
            }
            protected virtual void OnShapeChanged(MyEventArgs e)
            {
                if(ShapeChanged != null)
                {
                   ShapeChanged(this, e);
                }
            }
        }
    
    }
    

예제

다음 예제에서는 클래스가 두 개 이상의 인터페이스에서 상속되며 각 인터페이스에 동일한 이름의 이벤트가 있는 보다 덜 일반적인 경우를 처리하는 방법을 보여 줍니다. 이 경우 하나 이상의 이벤트에 대해 명시적으로 인터페이스를 구현해야 합니다. 이벤트에 대한 명시적 인터페이스 구현을 작성할 때는 add 및 remove 이벤트 접근자를 작성해야 합니다. 일반적으로 이러한 접근자는 컴파일러에서 제공되지만 이 경우에는 컴파일러에서 제공할 수 없습니다.

사용자 고유의 접근자를 제공하면 두 이벤트를 클래스의 동일한 이벤트가 나타낼지 각기 다른 이벤트가 나타낼지를 지정할 수 있습니다. 예를 들어, 인터페이스 사양에 따라 다른 시간에 이벤트를 발생시켜야 하는 경우에는 클래스에 있는 별도의 구현에 각 이벤트를 연결할 수 있습니다. 다음 예제에서는 구독자가 IShape 또는 IDrawingObject에 대한 모양 참조를 캐스팅하여 수신할 OnDraw 이벤트를 결정합니다.

namespace WrapTwoInterfaceEvents
{
    using System;

    public interface IDrawingObject
    {
        // Raise this event before drawing
        // the object.
        event EventHandler OnDraw;
    }
    public interface IShape
    {
        // Raise this event after drawing
        // the shape.
        event EventHandler OnDraw;
    }


    // Base class event publisher inherits two
    // interfaces, each with an OnDraw event
    public class Shape : IDrawingObject, IShape
    {
        // Create an event for each interface event
        event EventHandler PreDrawEvent;
        event EventHandler PostDrawEvent;

        object objectLock = new Object();

        // Explicit interface implementation required.
        // Associate IDrawingObject's event with
        // PreDrawEvent
        event EventHandler IDrawingObject.OnDraw
        {
            add
            {
                lock (objectLock)
                {
                    PreDrawEvent += value;
                }
            }
            remove
            {
                lock (objectLock)
                {
                    PreDrawEvent -= value;
                }
            }
        }
        // Explicit interface implementation required.
        // Associate IShape's event with
        // PostDrawEvent
        event EventHandler IShape.OnDraw
        {
            add
            {
                lock (objectLock)
                {
                    PostDrawEvent += value;
                }
            }
            remove
            {
                lock (objectLock)
                {
                    PostDrawEvent -= value;
                }
            }


        }

        // For the sake of simplicity this one method
        // implements both interfaces. 
        public void Draw()
        {
            // Raise IDrawingObject's event before the object is drawn.
            EventHandler handler = PreDrawEvent;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
            Console.WriteLine("Drawing a shape.");

            // RaiseIShape's event after the object is drawn.
            handler = PostDrawEvent;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }
    }
    public class Subscriber1
    {
        // References the shape object as an IDrawingObject
        public Subscriber1(Shape shape)
        {
            IDrawingObject d = (IDrawingObject)shape;
            d.OnDraw += new EventHandler(d_OnDraw);
        }

        void d_OnDraw(object sender, EventArgs e)
        {
            Console.WriteLine("Sub1 receives the IDrawingObject event.");
        }
    }
    // References the shape object as an IShape
    public class Subscriber2
    {
        public Subscriber2(Shape shape)
        {
            IShape d = (IShape)shape;
            d.OnDraw += new EventHandler(d_OnDraw);
        }

        void d_OnDraw(object sender, EventArgs e)
        {
            Console.WriteLine("Sub2 receives the IShape event.");
        }
    }


    public class Program
    {
        static void Main(string[] args)
        {
            Shape shape = new Shape();
            Subscriber1 sub = new Subscriber1(shape);
            Subscriber2 sub2 = new Subscriber2(shape);
            shape.Draw();

            // Keep the console window open in debug mode.
            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
    }

}
/* Output:
    Sub1 receives the IDrawingObject event.
    Drawing a shape.
    Sub2 receives the IShape event.
*/




이벤트


1. 클래스나 개체에서는 특정 상황이 발생할 때 이벤트를 통해 다른 클래스나 개체에 이를 알려 줄 수 있습니다.

2. 이벤트를 보내거나 발생시키는 클래스를 게시자(the publisher)라고 하며 이벤트를 받거나 처리하는 클래스를 구독자(subscribers)라고 합니다.

3. 일반적인 C# Windows Forms 또는 응용 프로그램에서는 단추 및 목록 상자와 같은 컨트롤에서 발생하는 이벤트를 구독합니다.

4. Visual C# IDE(통합 개발 환경)를 사용하여 컨트롤이 게시하는 이벤트를 찾고 처리할 이벤트를 선택할 수 있습니다.

5. IDE에서는 빈 이벤트 처리기 메서드와 이벤트를 구독할 수 있는 코드를 자동으로 추가합니다.

6. 자세한 내용은 방법: 이벤트 구독 및 구독 취소(C# 프로그래밍 가이드)를 참조하십시오.


이벤트 개요

이벤트에는 다음과 같은 속성이 있습니다.

1. 게시자는 이벤트가 발생하는 시기를 결정하고 구독자는 이벤트에 대한 응답으로 수행되는 동작을 결정합니다.

2. 한 이벤트에 여러 개의 구독자가 있을 수 있습니다. 구독자는 여러 게시자의 이벤트를 여러 가지 처리할 수 있습니다.

3. 구독자가 없는 이벤트는 발생되지 않습니다.

4. 이벤트는 일반적으로 그래픽 사용자 인터페이스에서 단추를 클릭하거나 메뉴를 선택하는 것과 같은 사용자 동작을 알리는 데 사용됩니다.

5. 이벤트에 여러 구독자가 있으면 해당 이벤트가 발생할 때 여러 이벤트 처리기기가 동기적으로 호출됩니다.

6. 이벤트를 비동기적으로 호출하려면 동기 메서드를 비동기 방식으로 호출을 참조하십시오.

7. .NET Framework 클래스 라이브러리에서 이벤트는 EventHandler 대리자 및 EventArgs 기본 클래스를 기반으로 합니다.



방법: 이벤트 구독 및 구독 취소


1. 다른 클래스에서 게시한 이벤트가 발생할 때 호출되는 사용자 지정 코드를 작성하려면 해당 이벤트를 구독합니다.

2. 예를 들어, 사용자가 단추를 클릭할 때 응용 프로그램에서 필요한 작업을 수행하도록 하려면 해당 단추의 click 이벤트를 구독합니다.


Visual Studio IDE를 사용하여 이벤트를 구독하려면

1. 속성 창이 표시되어 있지 않으면 디자인 뷰에서 이벤트 처리기를 만들려는 폼이나 컨트롤을 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.

2. 속성 창의 위쪽에서 이벤트 아이콘을 클릭합니다.

3. Load 이벤트 등의 만들려는 이벤트를 두 번 클릭합니다.

4. Visual C#에서 자동으로 빈 이벤트 처리기 메서드가 만들어져 코드에 추가됩니다.

5. 또는 사용자가 직접 코드 뷰에서 코드를 추가할 수 있습니다.

6. 예를 들어, 다음 코드 줄에서는 Form 클래스에서 Load 이벤트를 발생시킬 때 호출되는 이벤트 처리기 메서드를 선언합니다.


private void Form1_Load(object sender, System.EventArgs e)
{
    // Add your form load event handling code here.
}


7. 이벤트를 구독하는 데 필요한 코드 줄은 프로젝트의 Form1.Designer.cs 파일에 있는 InitializeComponent 메서드에서도 자동으로 생성됩니다. 이 코드 줄은 다음과 같습니다.


this.Load += new System.EventHandler(this.Form1_Load);



프로그래밍 방식으로 이벤트를 구독하려면


1. 시그니처가 해당 이벤트의 대리자 시그니처와 일치하는 이벤트 처리기 메서드를 정의합니다.

2. 예를 들어, 이벤트가 EventHandler 대리자 형식을 기반으로 하는 경우 다음 코드는 메서드 스텁을 나타냅니다.


void HandleCustomEvent(object sender, CustomEventArgs a)

{

   // Do something useful here.

}


3. 이벤트에 이벤트 처리기를 연결하려면 더하기 할당 연산자(+=)를 사용합니다.

4. 다음 예제에서는 publisher 개체에 RaiseCustomEvent 이벤트가 있다고 가정합니다.

5. 구독자 클래스에서 해당 이벤트를 구독하려면 게시자 클래스에 대한 참조가 필요합니다.


publisher.RaiseCustomEvent += HandleCustomEvent;


6. 위의 구문은 C# 2.0의 새로운 구문입니다.

7. 이 구문에 해당하는 C# 1.0 구문에서는 new 키워드를 사용하여 캡슐화 대리자를 명시적으로 만들어야 합니다.


publisher.RaiseCustomEvent += new CustomEventHandler(HandleCustomEvent);


8. 람다 식을 사용하여 이벤트 처리기를 추가할 수도 있습니다.


public Form1()

{

    InitializeComponent();

    // Use a lambda expression to define an event handler.

    this.Click += (s,e) => { MessageBox.Show(

       ((MouseEventArgs)e).Location.ToString());};

}


무명 메서드를 사용하여 이벤트를 구독하려면

1. 나중에 이벤트에 대한 등록을 취소할 필요가 없는 경우 더하기 할당 연산자(+=)를 사용하여 이벤트에 무명 메서드를 연결합니다.

2. 다음 예제에서는 publisher 개체에 RaiseCustomEvent 이벤트가 있으며 몇 가지 특수 이벤트 정보를 전달하도록 CustomEventArgs 클래스도 정의되었다고 가정합니다.

3. 구독자 클래스에서 해당 이벤트를 구독하려면 publisher에 대한 참조가 필요합니다.


publisher.RaiseCustomEvent += delegate(object o, CustomEventArgs e)

{

  string s = o.ToString() + " " + e.ToString();

  Console.WriteLine(s);

};


4. 익명 함수를 사용하여 이벤트를 구독한 경우 이벤트를 쉽게 구독 취소할 수 없다는 점에 주의하십시오.

5. 이 경우에 이벤트를 구독 취소하려면 이벤트를 구독하는 코드로 돌아가서 대리자 변수에 무명 메서드를 저장하고 이벤트에 대리자를 추가해야 합니다.

6. 일반적으로 코드의 뒷부분에서 이벤트에 대한 구독을 취소해야 한다면 이벤트를 구독하는 데 익명 함수를 사용하지 않는 것이 좋습니다.

7. 익명 함수에 대한 자세한 내용은 익명 함수(C# 프로그래밍 가이드)를 참조하십시오.


구독 취소


1. 이벤트가 발생할 때 이벤트 처리기가 호출되지 않도록 하려면 이벤트를 구독 취소합니다.

2. 리소스 누수를 방지하려면 구독자 개체를 삭제하기 전에 이벤트를 구독 취소해야 합니다.

3. 이벤트를 구독 취소하기 전까지 게시 개체에 있는 이벤트의 기반이 되는 멀티캐스트 대리자에는 구독자의 이벤트 처리기를 캡슐화하는 대리자에 대한 참조가 있습니다.

4. 게시 개체에 해당 참조가 포함되어 있는 한 가비지 수집에서 구독자 개체가 삭제되지 않습니다.


이벤트를 구독 취소하려면

1. 빼기 할당 연산자(-=)를 사용하여 이벤트를 구독 취소합니다.


publisher.RaiseCustomEvent -= HandleCustomEvent;


2. 모든 구독자가 이벤트를 구독 취소한 경우 게시자 클래스에 있는 해당 이벤트 인스턴스는 null로 설정됩니다.




방법: 인터페이스 이벤트 구현


1. 인터페이스에서는 이벤트를 선언할 수 있습니다.

2. 다음 예제에서는 클래스에 인터페이스 이벤트를 구현하는 방법을 보여 줍니다.

3. 기본적으로 규칙은 인터페이스 메서드나 속성을 구현할 때와 동일합니다.


클래스에 인터페이스 이벤트를 구현하려면

1. 클래스에 이벤트를 선언하고 이를 적절한 영역에서 호출합니다.


namespace ImplementInterfaceEvents
{
    public interface IDrawingObject
    {
        event EventHandler ShapeChanged;
    }
    public class MyEventArgs : EventArgs 
    {
        // class members
    }
    public class Shape : IDrawingObject
    {
        public event EventHandler ShapeChanged;
        void ChangeShape()
        {
            // Do something here before the event…

            OnShapeChanged(new MyEventArgs(/*arguments*/));

            // or do something here after the event. 
        }
        protected virtual void OnShapeChanged(MyEventArgs e)
        {
            if(ShapeChanged != null)
            {
               ShapeChanged(this, e);
            }
        }
    }

}


예제

1. 다음 예제에서는 클래스가 두 개 이상의 인터페이스에서 상속되며 각 인터페이스에 동일한 이름의 이벤트가 있는 보다 덜 일반적인 경우를 처리하는 방법을 보여 줍니다.

2. 이 경우 하나 이상의 이벤트에 대해 명시적으로 인터페이스를 구현해야 합니다.

3. 이벤트에 대한 명시적 인터페이스 구현을 작성할 때는 add 및 remove 이벤트 접근자를 작성해야 합니다.

3. 일반적으로 이러한 접근자는 컴파일러에서 제공되지만 이 경우에는 컴파일러에서 제공할 수 없습니다.

4. 사용자 고유의 접근자를 제공하면 두 이벤트를 클래스의 동일한 이벤트가 나타낼지 각기 다른 이벤트가 나타낼지를 지정할 수 있습니다.

5. 예를 들어, 인터페이스 사양에 따라 다른 시간에 이벤트를 발생시켜야 하는 경우에는 클래스에 있는 별도의 구현에 각 이벤트를 연결할 수 있습니다.

6. 다음 예제에서는 구독자가 IShape 또는 IDrawingObject에 대한 모양 참조를 캐스팅하여 수신할 OnDraw 이벤트를 결정합니다.


namespace WrapTwoInterfaceEvents
{
    using System;

    public interface IDrawingObject
    {
        // Raise this event before drawing
        // the object.
        event EventHandler OnDraw;
    }
    public interface IShape
    {
        // Raise this event after drawing
        // the shape.
        event EventHandler OnDraw;
    }


    // Base class event publisher inherits two
    // interfaces, each with an OnDraw event
    public class Shape : IDrawingObject, IShape
    {
        // Create an event for each interface event
        event EventHandler PreDrawEvent;
        event EventHandler PostDrawEvent;

        object objectLock = new Object();

        // Explicit interface implementation required.
        // Associate IDrawingObject's event with
        // PreDrawEvent
        event EventHandler IDrawingObject.OnDraw
        {
            add
            {
                lock (objectLock)
                {
                    PreDrawEvent += value;
                }
            }
            remove
            {
                lock (objectLock)
                {
                    PreDrawEvent -= value;
                }
            }
        }
        // Explicit interface implementation required.
        // Associate IShape's event with
        // PostDrawEvent
        event EventHandler IShape.OnDraw
        {
            add
            {
                lock (objectLock)
                {
                    PostDrawEvent += value;
                }
            }
            remove
            {
                lock (objectLock)
                {
                    PostDrawEvent -= value;
                }
            }


        }

        // For the sake of simplicity this one method
        // implements both interfaces. 
        public void Draw()
        {
            // Raise IDrawingObject's event before the object is drawn.
            EventHandler handler = PreDrawEvent;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
            Console.WriteLine("Drawing a shape.");

            // RaiseIShape's event after the object is drawn.
            handler = PostDrawEvent;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }
    }
    public class Subscriber1
    {
        // References the shape object as an IDrawingObject
        public Subscriber1(Shape shape)
        {
            IDrawingObject d = (IDrawingObject)shape;
            d.OnDraw += new EventHandler(d_OnDraw);
        }

        void d_OnDraw(object sender, EventArgs e)
        {
            Console.WriteLine("Sub1 receives the IDrawingObject event.");
        }
    }
    // References the shape object as an IShape
    public class Subscriber2
    {
        public Subscriber2(Shape shape)
        {
            IShape d = (IShape)shape;
            d.OnDraw += new EventHandler(d_OnDraw);
        }

        void d_OnDraw(object sender, EventArgs e)
        {
            Console.WriteLine("Sub2 receives the IShape event.");
        }
    }


    public class Program
    {
        static void Main(string[] args)
        {
            Shape shape = new Shape();
            Subscriber1 sub = new Subscriber1(shape);
            Subscriber2 sub2 = new Subscriber2(shape);
            shape.Draw();

            // Keep the console window open in debug mode.
            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
    }

}
/* Output:
    Sub1 receives the IDrawingObject event.
    Drawing a shape.
    Sub2 receives the IShape event.
*/





인덱서



인덱서 사용

인덱서는 클라이언트 응용 프로그램에서 배열과 마찬가지 방식으로 액세스할 수 있는 클래스구조체 또는 인터페이스를 만들 수 있게 해 주는 편리한 구문입니다. 인덱서는 주로 내부 컬렉션 또는 배열을 캡슐화하기 위한 형식에서 가장 많이 구현됩니다. 예를 들어 24시간 동안 10번 기록되는 온도를 화씨로 나타내는 TempRecord라는 클래스가 있다고 가정합니다. 클래스에는 온도를 나타내는 float 형식의 "temps"라는 배열과 온도를 기록한 날짜를 나타내는 DateTime이 들어 있습니다. 이 경우 인덱서를 구현하면 클라이언트는 float temp = tr.temps[4] 대신 float temp = tr[4]로 TempRecord 인스턴스의 온도에 액세스할 수 있습니다. 인덱서 표기법은 클라이언트 응용 프로그램의 구문을 단순하게 할 뿐 아니라 클래스와 해당 클래스의 용도를 다른 개발자가 쉽게 이해할 수 있도록 합니다.

클래스나 구조체에 대한 인덱서를 선언하려면 다음 예제에서와 같이 this 키워드를 사용합니다.

public int this[int index]    // Indexer declaration
{
    // get and set accessors
}

인덱서 형식과 해당 매개 변수 형식에는 적어도 인덱서 자체와 동등한 수준으로 액세스할 수 있어야 합니다. 액세스 가능성 수준에 대한 자세한 내용은 액세스 한정자를 참조하십시오.

인터페이스와 함께 인덱서를 사용하는 방법에 대한 자세한 내용은 인터페이스 인덱서를 참조하십시오.

인덱서 시그니처는 인덱서 정식 매개 변수의 수 및 형식으로 구성됩니다. 이 시그니처는 인덱서 형식 또는 정식 매개 변수 이름을 포함하지 않습니다. 같은 클래스에 두 개 이상의 인덱서를 선언한 경우, 모든 인덱서가 다른 시그니처를 가져야 합니다.

인덱서 값은 변수로 분류되지 않기 때문에 인덱서 값을 ref 또는 out 매개 변수로 전달할 수 없습니다.

다른 언어에서 사용할 수 있는 이름을 인덱서에 부여하려면 선언에 name 특성을 사용합니다. 예를 들면 다음과 같습니다.

[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this [int index]   // Indexer declaration
{
}

이 인덱서의 이름은 TheItem입니다. name 특성을 제공하지 않으면 기본 이름으로 Item이 사용됩니다.

다음 예제는 전용 배열 필드 temps와 인덱서를 선언하는 방법을 보여 줍니다. 인덱서를 사용하면 tempRecord[i] 인스턴스에 직접 액세스할 수 있습니다. 인덱서를 사용하지 않으려면 배열을 public멤버로 선언하고 해당 멤버인 tempRecord.temps[i]에 직접 액세스하면 됩니다.

예를 들어, Console.Write 문에서 인덱서의 액세스를 계산할 경우에는 get 접근자가 호출됩니다. 따라서 get 접근자가 없으면 컴파일 타임 오류가 발생합니다.

class TempRecord
{
    // Array of temperature values
    private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F, 
                                            61.3F, 65.9F, 62.1F, 59.2F, 57.5F };

    // To enable client code to validate input 
    // when accessing your indexer.
    public int Length
    {
        get { return temps.Length; }
    }
    // Indexer declaration.
    // If index is out of range, the temps array will throw the exception.
    public float this[int index]
    {
        get
        {
            return temps[index];
        }

        set
        {
            temps[index] = value;
        }
    }
}

class MainClass
{
    static void Main()
    {
        TempRecord tempRecord = new TempRecord();
        // Use the indexer's set accessor
        tempRecord[3] = 58.3F;
        tempRecord[5] = 60.1F;

        // Use the indexer's get accessor
        for (int i = 0; i < 10; i++)
        {
            System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
        }

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();

    }
}
/* Output:
        Element #0 = 56.2
        Element #1 = 56.7
        Element #2 = 56.5
        Element #3 = 58.3
        Element #4 = 58.8
        Element #5 = 60.1
        Element #6 = 65.9
        Element #7 = 62.1
        Element #8 = 59.2
        Element #9 = 57.5
    */

C#에서는 인덱스 형식이 정수로만 제한되지 않습니다. 예를 들어, 인덱서와 함께 문자열을 사용하는 것이 유용할 수 있습니다. 이러한 인덱서는 컬렉션에서 문자열을 검색하고 적절한 값을 반환하여 구현할 수 있습니다. 접근자로 오버로드할 수 있으므로 문자열과 정수 버전의 인덱스 형식은 함께 사용될 수 있습니다.

이 예제에서는 요일을 저장하는 클래스를 선언합니다. 문자열과 요일 이름을 가져오고 상응하는 정수를 반환하는 get 접근자를 선언합니다. 예를 들어, 일요일은 0을 반환하고 월요일은 1을 반환하는 방식입니다.

// Using a string as an indexer value
class DayCollection
{
    string[] days = { "Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat" };

    // This method finds the day or returns -1
    private int GetDay(string testDay)
    {

        for (int j = 0; j < days.Length; j++)
        {
            if (days[j] == testDay)
            {
                return j;
            }
        }

        throw new System.ArgumentOutOfRangeException(testDay, "testDay must be in the form \"Sun\", \"Mon\", etc");
    }

    // The get accessor returns an integer for a given string
    public int this[string day]
    {
        get
        {
            return (GetDay(day));
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        DayCollection week = new DayCollection();
        System.Console.WriteLine(week["Fri"]);

        // Raises ArgumentOutOfRangeException
        System.Console.WriteLine(week["Made-up Day"]);

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }
}
// Output: 5

인덱서의 보안과 안전성을 향상시키는 데는 크게 두 가지 방법이 있습니다.

  • 클라이언트 코드에서 잘못된 인덱스 값을 전달할 경우를 처리하려면 몇 가지 오류 처리 방법을 통합해야 합니다. 이 항목의 앞부분에 나오는 첫 번째 예제에서 TempRecord 클래스는 클라이언트 코드에서 입력 내용을 인덱서에 전달하기 전에 해당 입력을 확인하는 데 사용할 수 있는 Length 속성을 제공합니다. 오류 처리 코드를 인덱서 내에 포함할 수도 있습니다. 인덱서 접근자 내에서 throw하는 모든 예외에 대해 사용자에게 설명해야 합니다.

  • get 및 set 접근자의 액세스 가능성을 적절한 수준에서 제한적으로 설정합니다. 이는 특히 set 접근자의 경우 중요한 의미를 갖습니다. 자세한 내용은 접근자 액세스 가능성 제한(C# 프로그래밍 가이드)를 참조하십시오.



인터페이스의 인덱서


게시: 2016년 4월

interface(C# 참조)에서 인덱서를 선언할 수 있습니다. 인터페이스 인덱서의 접근자와 클래스 인덱서의 접근자는 다음과 같은 차이점이 있습니다.

  • 인터페이스 접근자는 한정자를 사용하지 않습니다.

  • 인터페이스 접근자에는 본문이 없습니다.

따라서 접근자의 목적은 인덱서가 읽기/쓰기, 읽기 전용 또는 쓰기 전용인지 여부를 나타내는 것입니다.

다음은 인터페이스 인덱서 접근자의 예제입니다.

public interface ISomeInterface
{
    //...

    // Indexer declaration:
    string this[int index]
    {
        get;
        set;
    }
}

인덱서의 시그니처는 같은 인터페이스에 선언된 다른 모든 인덱서의 시그니처와 달라야 합니다.

예제

다음 예제는 인터페이스 인덱서의 구현 방법을 보여 줍니다.

// Indexer on an interface:
public interface ISomeInterface
{
    // Indexer declaration:
    int this[int index]
    {
        get;
        set;
    }
}

// Implementing the interface.
class IndexerClass : ISomeInterface
{
    private int[] arr = new int[100];
    public int this[int index]   // indexer declaration
    {
        get
        {
            // The arr object will throw IndexOutOfRange exception.
            return arr[index];
        }
        set
        {
            arr[index] = value;
        }
    }
}

class MainClass
{
    static void Main()
    {
        IndexerClass test = new IndexerClass();
        System.Random rand = new System.Random();
        // Call the indexer to initialize its elements.
        for (int i = 0; i < 10; i++)
        {
            test[i] = rand.Next();
        }
        for (int i = 0; i < 10; i++)
        {
            System.Console.WriteLine("Element #{0} = {1}", i, test[i]);
        }

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }
}
/* Sample output:
    Element #0 = 360877544
    Element #1 = 327058047
    Element #2 = 1913480832
    Element #3 = 1519039937
    Element #4 = 601472233
    Element #5 = 323352310
    Element #6 = 1422639981
    Element #7 = 1797892494
    Element #8 = 875761049
    Element #9 = 393083859
 */


위 예제에서 인터페이스 멤버의 정규화된 이름을 사용하여 명시적 인터페이스 멤버를 구현할 수 있습니다. 예를 들면 다음과 같습니다.

public string ISomeInterface.this 
{ 
} 

그러나 정규화된 이름은 클래스에서 인덱서 시그니처가 같은 두 개 이상의 인터페이스를 구현하는 경우에 모호성을 피하고자 할 때 필요합니다. 예를 들어, Employee 클래스가 ICitizen 및 IEmployee라는 두 개의 인터페이스를 구현하고 두 인터페이스에 동일한 인덱서 시그니처가 있는 경우 해당 인터페이스 멤버를 명시적으로 구현해야 합니다. 예를 들면 다음과 같습니다.

public string IEmployee.this 
{ 
} 

위의 선언은 IEmployee 인터페이스에서 인덱서를 구현합니다.

public string ICitizen.this 
{ 
}  

그러나 위의 선언은 ICitizen 인터페이스에서 인덱서를 구현합니다.



속성 및 인덱서 비교


인덱서는 속성과 비슷합니다. 다음 표에 나와 있는 차이점을 제외하면 접근자에 정의된 모든 규칙이 인덱서 접근자에도 적용됩니다.

Property

인덱서

공용 데이터 멤버인 것처럼 메서드를 호출할 수 있습니다.

개체 자체의 배열 표기법을 사용하여 개체의 내부 컬렉션 요소에 액세스할 수 있습니다.

단순한 이름을 통해 액세스할 수 있습니다.

인덱스를 통해 액세스할 수 있습니다.

정적 또는 인스턴스 멤버가 될 수 있습니다.

인스턴스 멤버여야 합니다.

속성의 get 접근자에는 매개 변수가 없습니다.

인덱서의 get 접근자는 인덱서와 동일한 정식 매개 변수 목록을 가집니다.

속성의 set 접근자에는 명시적인 value 매개 변수가 포함됩니다.

인덱서의 set 접근자는 value 매개 변수 외에도 인덱서와 동일한 정식 매개 변수 목록을 가집니다.

자동으로 구현된 속성(C# 프로그래밍 가이드)이 있는 약식 구문을 지원합니다.

약식 구문을 지원하지 않습니다.






'프로그래밍 > C#' 카테고리의 다른 글

형식 참조 테이블  (0) 2016.08.23
클래스 및 구조체  (0) 2016.08.05
제네릭 요약  (0) 2016.08.02
LINQ 정리  (1) 2016.07.28
형 변환 예제  (0) 2016.07.10
:
Posted by 지훈2