수색…
인터페이스 구현하기
인터페이스는 그것을 구현하는 모든 클래스에서 메소드의 존재를 강제하는 데 사용됩니다. 인터페이스는 키워드로 정의 된 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
를 구현하기 때문에 cat
와 dog
모두 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.Drive
와 Drive
는 가능한 경우 동일한 작업을 수행해야합니다.
인터페이스를 사용하는 이유
인터페이스는 인터페이스 사용자와 인터페이스를 구현하는 클래스 간의 계약을 정의한 것입니다. 인터페이스를 생각하는 한 가지 방법은 객체가 특정 기능을 수행 할 수 있다는 선언입니다.
다른 유형의 도형을 나타 내기 위해 인터페이스 IShape
를 정의한다고 가정 해 봅시다. 도형에 영역이 있어야하기 때문에 인터페이스 구현이 해당 영역을 반환하도록하는 메서드를 정의합니다.
public interface IShape
{
double ComputeArea();
}
다음 두 가지 모양을 갖도록하겠습니다 : Rectangle
과 Circle
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 인터페이스 구현의 예
인터페이스는 추상적 인 것처럼 보일 수 있습니다. IComparable
과 IComparable<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()
를 호출하면 객체를 넣을 순서를 결정할 때 List
가 Item
의 CompareTo
메서드를 자동으로 호출합니다. 또한 List<T>
외에 다른 객체 그것은 작동합니다 두 객체 비교 할 수있는 능력이 필요 Item
우리는 두 가지의 기능을 정의하기 때문에 Item
의이 서로 비교한다.