수색…


인터페이스 구현하기

인터페이스는 그것을 구현하는 모든 클래스에서 메소드의 존재를 강제하는 데 사용됩니다. 인터페이스는 키워드로 정의 된 interface 와 클래스를 추가하여 '실행'할 수 있습니다 : InterfaceName 클래스 이름 뒤에. 클래스는 각 인터페이스를 쉼표로 분리하여 여러 인터페이스를 구현할 수 있습니다.
: InterfaceName, ISecondInterface

public interface INoiseMaker
{
    string MakeNoise();
}

public class Cat : INoiseMaker
{
    public string MakeNoise()
    {
        return "Nyan";
    }
}

public class Dog : INoiseMaker
{
    public string MakeNoise()
    {
        return "Woof";
    }
}

INoiseMaker 를 구현하기 때문에 catdog 모두 string MakeNoise() 메서드 string MakeNoise() 을 포함해야 string MakeNoise() 메서드를 사용하지 않고 컴파일하지 못합니다.

다중 인터페이스 구현하기

public interface IAnimal 
{
    string Name { get; set; }
}

public interface INoiseMaker
{
    string MakeNoise();
}

public class Cat : IAnimal, INoiseMaker
{
    public Cat() 
    {
        Name = "Cat";
    }

    public string Name { get; set; }

    public string MakeNoise()
    {
        return "Nyan";
    }
}

명시 적 인터페이스 구현

명시 적 인터페이스 구현은 일반적인 메소드를 정의하는 여러 인터페이스를 구현할 때 필요하지만 메소드 호출에 사용되는 인터페이스에 따라 다른 구현이 필요합니다. 여러 인터페이스가 동일한 메소드를 공유하고 명시 적 구현이 필요하지 않으며 공통 구현이 가능하다).

interface IChauffeur 
{
    string Drive();
}

interface IGolfPlayer
{
    string Drive();
}

class GolfingChauffeur : IChauffeur, IGolfPlayer 
{
    public string Drive()
    {
        return "Vroom!";
    }

    string IGolfPlayer.Drive()
    {
        return "Took a swing...";
    }
}


GolfingChauffeur obj = new GolfingChauffeur();
IChauffeur chauffeur = obj;
IGolfPlayer golfer = obj;

Console.WriteLine(obj.Drive()); // Vroom!
Console.WriteLine(chauffeur.Drive()); // Vroom!
Console.WriteLine(golfer.Drive()); // Took a swing...

구현은 인터페이스를 사용하는 것을 제외하고는 어디에서도 호출 할 수 없습니다.

public class Golfer : IGolfPlayer
{
    string IGolfPlayer.Drive()
    {
        return "Swinging hard...";
    }
    public void Swing()
    {
        Drive(); // Compiler error: No such method
    }
}

이 때문에 명시 적으로 구현 된 인터페이스의 복잡한 구현 코드를 별도의 개인 메서드에 넣는 것이 유리할 수 있습니다.

명시 적 인터페이스 구현은 물론 해당 인터페이스에 실제로 존재하는 메소드에만 사용할 수 있습니다.

public class ProGolfer : IGolfPlayer
{
    string IGolfPlayer.Swear() // Error
    {
        return "The ball is in the pit";
    }
}

마찬가지로 클래스에 해당 인터페이스를 선언하지 않고 명시 적 인터페이스 구현을 사용하면 오류도 발생합니다.

힌트:

명시 적으로 인터페이스를 구현하면 데드 코드를 피할 수도 있습니다. 메소드가 더 이상 필요하지 않고 인터페이스에서 제거되면 컴파일러는 각각의 기존 구현에 대해 불평 할 것입니다.

노트 :

프로그래머는 형식의 컨텍스트에 관계없이 계약이 동일 할 것으로 기대하며 명시 적 구현은 호출시 다른 동작을 노출해서는 안됩니다. 위의 예와 달리 IGolfPlayer.DriveDrive 는 가능한 경우 동일한 작업을 수행해야합니다.

인터페이스를 사용하는 이유

인터페이스는 인터페이스 사용자와 인터페이스를 구현하는 클래스 간의 계약을 정의한 것입니다. 인터페이스를 생각하는 한 가지 방법은 객체가 특정 기능을 수행 할 수 있다는 선언입니다.

다른 유형의 도형을 나타 내기 위해 인터페이스 IShape 를 정의한다고 가정 해 봅시다. 도형에 영역이 있어야하기 때문에 인터페이스 구현이 해당 영역을 반환하도록하는 메서드를 정의합니다.

public interface IShape
{
    double ComputeArea();
}

다음 두 가지 모양을 갖도록하겠습니다 : RectangleCircle

public class Rectangle : IShape
{
    private double length;
    private double width;

    public Rectangle(double length, double width)
    {
        this.length = length;
        this.width = width;
    }

    public double ComputeArea()
    {
        return length * width;
    }
}

public class Circle : IShape
{
    private double radius;

    public Circle(double radius)
    {
        this.radius = radius;
    }

    public double ComputeArea()
    {
        return Math.Pow(radius, 2.0) * Math.PI;
    }
}

그들 각각은 지역에 대한 자체 정의를 가지고 있지만, 둘 다 모양입니다. 따라서 프로그램에서 IShape 로 간주하는 것은 논리적 인 것입니다.

private static void Main(string[] args)
{
    var shapes = new List<IShape>() { new Rectangle(5, 10), new Circle(5) };
    ComputeArea(shapes);

    Console.ReadKey();
}

private static void ComputeArea(IEnumerable<IShape> shapes) 
{
    foreach (shape in shapes)
    {
        Console.WriteLine("Area: {0:N}, shape.ComputeArea());
    }
}

// Output:
// Area : 50.00
// Area : 78.54

인터페이스 기본 사항

기능의 "계약"이라고 알려진 인터페이스의 기능. 즉, 속성과 메서드를 선언하지만 구현하지는 않습니다.

그래서 클래스 인터페이스와 달리 :

  • 인스턴스화 할 수 없습니다.
  • 기능을 사용할 수 없습니다.
  • 메소드 만 포함 할 수 있음 * (속성 및 이벤트는 내부적으로 메소드 임)
  • 인터페이스 상속을 "구현"
  • 1 클래스에서 상속받을 수 있지만 여러 인터페이스를 "구현"할 수 있습니다.
public interface ICanDoThis{
    void TheThingICanDo();
    int SomeValueProperty { get; set; }
}

주의 사항 :

  • "I"접두어는 인터페이스에 사용되는 명명 규칙입니다.
  • 함수 본문은 세미콜론 ";"으로 대체됩니다.
  • 속성은 내부적으로 메소드이기도하므로 허용됩니다.
public class MyClass : ICanDoThis {
    public void TheThingICanDo(){
        // do the thing
    }

    public int SomeValueProperty { get; set; }
    public int SomeValueNotImplemtingAnything { get; set; }
}

.

ICanDoThis obj = new MyClass();

// ok
obj.TheThingICanDo();

// ok
obj.SomeValueProperty = 5;

// Error, this member doesn't exist in the interface
obj.SomeValueNotImplemtingAnything = 5;

// in order to access the property in the class you must "down cast" it
((MyClass)obj).SomeValueNotImplemtingAnything = 5; // ok

이는 기본 클래스에서 상속 받아 사용자 정의 컨트롤을 만들고 다른 컨트롤 유형에 대한 추상화 기능을 사용하지 않아야하므로 WinForms 또는 WPF와 같은 UI 프레임 워크로 작업 할 때 특히 유용합니다. 예를 들면? 올 때 :

public class MyTextBlock : TextBlock {
    public void SetText(string str){
        this.Text = str;
    }
}

public class MyButton : Button {
    public void SetText(string str){
        this.Content = str;
    }
}

제안 된 문제는 둘 다 "텍스트"의 개념을 포함하지만 속성 이름이 다릅니다. 그리고 두 가지 클래스에 대한 상속이 필수적이기 때문에 추상 기본 클래스를 만들 수 없습니다. 인터페이스가

public interface ITextControl{
    void SetText(string str);
}

public class MyTextBlock : TextBlock, ITextControl {
    public void SetText(string str){
        this.Text = str;
    }
}

public class MyButton : Button, ITextControl {
    public void SetText(string str){
        this.Content = str;
    }

    public int Clicks { get; set; }
}

이제 MyButton과 MyTextBlock은 서로 바꿔 사용할 수 있습니다.

var controls = new List<ITextControls>{
    new MyTextBlock(),
    new MyButton()
};

foreach(var ctrl in controls){
    ctrl.SetText("This text will be applied to both controls despite them being different");


    // Compiler Error, no such member in interface
    ctrl.Clicks = 0;

    // Runtime Error because 1 class is in fact not a button which makes this cast invalid
    ((MyButton)ctrl).Clicks = 0;


    /* the solution is to check the type first.
    This is usually considered bad practice since
    it's a symptom of poor abstraction */
    var button = ctrl as MyButton;
    if(button != null)
        button.Clicks = 0; // no errors

   
}

명시 적 구현으로 "숨기기"멤버

인터페이스가 당신을 걱정하지 않는 너무 많은 멤버와 함께 수업을 오염시킬 때 당신이 그것을 싫어하지 않습니까? 그럼 해결책이 있어요! 명시 적 구현

public interface IMessageService {
    void OnMessageRecieve();
    void SendMessage();
    string Result { get; set; }
    int Encoding { get; set; }
    // yadda yadda
}

일반적으로 이와 같은 클래스를 구현합니다.

public class MyObjectWithMessages : IMessageService {
     public void OnMessageRecieve(){

     }

     public void SendMessage(){

     }

     public string Result { get; set; }
     public int Encoding { get; set; }
}

모든 회원은 공개됩니다.

var obj = new MyObjectWithMessages();

// why would i want to call this function?
obj.OnMessageRecieve();

답변 : 그렇지 않습니다. 따라서 공개 선언을하지 않아도되지만 단순히 멤버를 비공개로 선언하면 컴파일러에서 오류가 발생합니다.

해결 방법은 명시 적 구현을 ​​사용하는 것입니다.

public class MyObjectWithMessages : IMessageService{
    void IMessageService.OnMessageRecieve() {
        
    }

    void IMessageService.SendMessage() {
        
    }

    string IMessageService.Result { get; set; }
    int IMessageService.Encoding { get; set; }
}

이제는 필요에 따라 회원을 구현 했으므로 멤버를 공개하지 않습니다.

var obj = new MyObjectWithMessages();

/* error member does not exist on type MyObjectWithMessages. 
 * We've succesfully made it "private" */
obj.OnMessageRecieve();

명시 적으로 구현 된 경우에도 멤버에 액세스하려는 경우, 인터페이스에 객체를 캐스트하면 좋을 것입니다.

((IMessageService)obj).OnMessageRecieve();

IComparable 인터페이스 구현의 예

인터페이스는 추상적 인 것처럼 보일 수 있습니다. IComparableIComparable<T> 는 인터페이스가 우리에게 도움이되는 이유의 좋은 예입니다.

온라인 상점 프로그램에서 구매할 수있는 다양한 상품이 있다고 가정 해 보겠습니다. 각 항목에는 이름, ID 번호 및 가격이 있습니다.

public class Item {
    
    public string name; // though public variables are generally bad practice,
    public int idNumber; // to keep this example simple we will use them instead
    public decimal price; // of a property.

    // body omitted for brevity        

}

우리는 Item List<Item> 안에 저장하고, 우리 프로그램에서는 어딘가에서 ID 번호별로 목록을 정렬하려고합니다. 독자적인 소트 알고리즘을 쓰는 대신, List<T> 이미있는 Sort() 메소드를 사용할 수 있습니다. 그러나 우리의 Item 클래스가 바로 지금이므로 List<T> 을 정렬하는 순서를 List<T> 가 이해할 방법이 없습니다. 여기에 IComparable 인터페이스가 들어 있습니다.

정확하게 구현할 CompareTo 방법을 CompareTo 파라미터 "이하"현재 하나가 동일하고, 음수 으면 0이 파라미터는 "보다 큰"이면이면 양수를 리턴한다.

Item apple = new Item();
apple.idNumber = 15;
Item banana = new Item();
banana.idNumber = 4;
Item cow = new Item();
cow.idNumber = 15;
Item diamond = new Item();
diamond.idNumber = 18;

Console.WriteLine(apple.CompareTo(banana)); // 11
Console.WriteLine(apple.CompareTo(cow)); // 0
Console.WriteLine(apple.CompareTo(diamond)); // -3

다음은 인터페이스의 Item 구현 예제입니다.

public class Item : IComparable<Item> {
    
    private string name;
    private int idNumber;
    private decimal price;

    public int CompareTo(Item otherItem) {

        return (this.idNumber - otherItem.idNumber);

    }

    // rest of code omitted for brevity    

}

서페이스 레벨에서 우리 항목의 CompareTo 메서드는 단순히 ID 번호의 차이를 반환하지만 실제로는 위의 작업은 무엇입니까?

이제 List<Item> 객체에서 Sort() 를 호출하면 객체를 넣을 순서를 결정할 때 ListItemCompareTo 메서드를 자동으로 호출합니다. 또한 List<T> 외에 다른 객체 그것은 작동합니다 두 객체 비교 할 수있는 능력이 필요 Item 우리는 두 가지의 기능을 정의하기 때문에 Item 의이 서로 비교한다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow