델리게이트와 이벤트의 차이점은 무엇입니까? 둘 다 실행할 수있는 함수에 대한 참조를 보유하지 않습니까?
답변
이벤트 선언은에 추상화와 보호 계층이 추가 대리자 인스턴스를. 이 보호 기능은 대리자의 클라이언트가 대리자와 호출 목록을 재설정하지 못하도록하며 호출 목록에서 대상을 추가하거나 제거 할 수 있습니다.
답변
차이점을 이해하려면이 두 가지 예를 살펴보십시오.
대리인의 예 (이 경우 Action-값을 반환하지 않는 일종의 대리인 임)
public class Animal
{
public Action Run {get; set;}
public void RaiseEvent()
{
if (Run != null)
{
Run();
}
}
}
대리인을 사용하려면 다음과 같이해야합니다.
Animal animal= new Animal();
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running") ;
animal.RaiseEvent();
이 코드는 잘 작동하지만 약한 부분이있을 수 있습니다.
예를 들어, 이것을 쓰면 :
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running");
animal.Run = () => Console.WriteLine("I'm sleeping") ;
마지막 코드 줄을 +
사용 하여 하나의 누락으로 이전 동작을 재정의했습니다 ( =
대신 사용 했습니다 +=
)
또 다른 약점은 당신의 Animal
클래스 를 사용하는 모든 클래스가 RaiseEvent
단지 그것을 호출 할 수 있다는 것 animal.RaiseEvent()
입니다.
이러한 약점을 피하기 위해 events
c #에서 사용할 수 있습니다 .
귀하의 동물 수업은 다음과 같이 변경됩니다 :
public class ArgsSpecial : EventArgs
{
public ArgsSpecial (string val)
{
Operation=val;
}
public string Operation {get; set;}
}
public class Animal
{
// Empty delegate. In this way you are sure that value is always != null
// because no one outside of the class can change it.
public event EventHandler<ArgsSpecial> Run = delegate{}
public void RaiseEvent()
{
Run(this, new ArgsSpecial("Run faster"));
}
}
이벤트를 호출
Animal animal= new Animal();
animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
animal.RaiseEvent();
차이점 :
- 공용 속성을 사용하지 않고 공용 필드를 사용하고 있습니다 (이벤트를 사용하면 컴파일러가 필드를 원치 않는 액세스로부터 보호 함)
- 이벤트는 직접 할당 할 수 없습니다. 이 경우 동작을 재정 의하여 표시했던 이전 오류가 발생하지 않습니다.
- 수업 이외의 어느 누구도 이벤트를 제기 할 수 없습니다.
- 이벤트는 인터페이스 선언에 포함될 수 있지만 필드는
노트:
EventHandler는 다음 대리자로 선언됩니다.
public delegate void EventHandler (object sender, EventArgs e)
발신자 (객체 유형)와 이벤트 인수가 필요합니다. 정적 메소드에서 온 발신인은 널입니다.
을 사용하는이 예제는 대신 EventHandler<ArgsSpecial>
사용하여 작성할 수도 있습니다 EventHandler
.
답변
구문 및 운영 속성 외에도 의미 상 차이가 있습니다.
대표자는 개념적으로 기능 템플릿입니다. 즉, 대리인의 “유형”으로 간주 되려면 기능이 준수해야하는 계약을 표현합니다.
이벤트는 … 잘, 이벤트를 나타냅니다. 그들은 어떤 일이 발생할 때 누군가에게 경고하기위한 것이며, 예, 대리인 정의를 고수하지만 같은 것은 아닙니다.
그것들이 정확히 같은 것 (구문 적으로 그리고 IL 코드에서)이더라도 의미상의 차이는 여전히 남아 있습니다. 일반적으로 동일한 방식으로 구현 된 경우에도 두 개의 서로 다른 개념에 대해 서로 다른 두 개의 이름을 사용하는 것이 좋습니다 (동일한 코드를 두 번 사용하는 것을 의미하지는 않습니다).
답변
여기에 또 다른 좋은 링크가 있습니다.
http://csharpindepth.com/Articles/Chapter2/Events.aspx
간단히 말해서 기사에서 빼앗아-이벤트는 대리인을 캡슐화합니다.
기사에서 인용 :
이벤트가 C # /. NET에 개념으로 존재하지 않았다고 가정하십시오. 다른 클래스는 어떻게 이벤트를 구독합니까? 세 가지 옵션 :
공개 델리게이트 변수
속성으로 뒷받침되는 델리게이트 변수
AddXXXHandler 및 RemoveXXXHandler 메소드가있는 델리게이트 변수
옵션 1은 명백히 끔찍한 데, 우리가 공개 변수를 싫어하는 모든 일반적인 이유 때문입니다.
옵션 2가 약간 나아지지만 구독자가 서로를 대체 할 수 있습니다. someInstance를 작성하기가 너무 쉽습니다 .MyEvent = eventHandler; 새 이벤트 핸들러를 추가하지 않고 기존 이벤트 핸들러를 대체합니다. 또한 속성을 작성해야합니다.
옵션 3은 기본적으로 이벤트가 제공하는 것이지만 필드와 유사한 이벤트가 제공하는 의미에 만족하는 경우 보장 된 규칙 (컴파일러에서 생성하고 IL에서 추가 플래그로 지원)과 “무료”구현을 사용합니다. 이벤트 처리기 목록에 대한 임의의 액세스를 허용하지 않고 이벤트 구독 및 구독 취소가 캡슐화되며 언어는 선언 및 구독에 대한 구문을 제공하여 작업을 단순화 할 수 있습니다.
답변
참고 : C # 5.0 Unleashed에 액세스 할 수있는 경우 “이벤트”라는 제목의 18 장에서 “대리인의 일반 사용에 대한 제한 사항”을 읽고 두 항목의 차이점을 더 잘 이해하십시오.
항상 간단하고 구체적인 예를 갖는 데 도움이됩니다. 여기 커뮤니티를위한 것이 있습니다. 먼저 델리게이트 만 사용하여 이벤트가 우리를 위해하는 일을 수행하는 방법을 보여줍니다. 그런 다음 동일한 솔루션이의 인스턴스와 어떻게 작동하는지 보여줍니다 EventHandler
. 그런 다음 첫 번째 예에서 설명하지 않은 이유를 설명합니다. 이 게시물은 기사 에서 영감을 얻었습니다. John Skeet .
예 1 : 공개 대리인 사용
단일 드롭 다운 상자가있는 WinForms 앱이 있다고 가정합니다. 드롭 다운이에 바인딩됩니다 List<Person>
. 여기서 Person은 Id, Name, NickName, HairColor의 속성을 갖습니다. 기본 양식에는 해당 개인의 속성을 표시하는 사용자 지정 사용자 정의 컨트롤이 있습니다. 드롭 다운에서 사람을 선택하면 사용자 컨트롤의 레이블이 업데이트되어 선택한 사람의 속성이 표시됩니다.
작동 방식은 다음과 같습니다. 이를 정리하는 데 도움이되는 세 개의 파일이 있습니다.
- Mediator.cs-정적 클래스는 대리자를 보유합니다.
- Form1.cs-기본 양식
- DetailView.cs-사용자 컨트롤은 모든 세부 사항을 보여줍니다
각 클래스에 대한 관련 코드는 다음과 같습니다.
class Mediator
{
public delegate void PersonChangedDelegate(Person p); //delegate type definition
public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this.
public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes.
{
if (PersonChangedDel != null)
{
PersonChangedDel(p);
}
}
}
다음은 사용자 컨트롤입니다.
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.PersonChangedDel += DetailView_PersonChanged;
}
void DetailView_PersonChanged(Person p)
{
BindData(p);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
마지막으로 Form1.cs에 다음 코드가 있습니다. 여기서는 OnPersonChanged를 호출하여 델리게이트에 가입 된 모든 코드를 호출합니다.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.OnPersonChanged((Person)comboBox1.SelectedItem); //Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.
}
확인. 이 작업을 얻을 것 어떻게의 그래서 이벤트를 사용하지 않고 및 위양을 사용 . 우리는 공개 델리게이트를 클래스에 넣었습니다. 정적 또는 싱글 톤 등 무엇이든 만들 수 있습니다. 큰.
그러나 우리는 위에서 설명한 것을하고 싶지 않습니다. 때문에 공공 분야는 나쁜 많은, 많은 이유. 그래서 우리의 선택은 무엇입니까? John Skeet이 설명했듯이 옵션은 다음과 같습니다.
- 공개 델리게이트 변수 (이것은 우리가 위에서 한 것입니다. 이것을하지 마십시오. 나는 왜 그것이 나쁜지에 대해 이야기했습니다)
- 대리자를 get / set을 사용하여 속성에 넣습니다. 여기서 문제는 구독자가 서로 재정의 할 수 있다는 것입니다. 따라서 대리자에게 여러 가지 메서드를 구독 할 수 있으며 실수로
PersonChangedDel = null
로“다른 구독을 모두 제거 있습니다. 여기에 남아있는 다른 문제는 사용자가 대리인에게 액세스 할 수 있기 때문에 호출 목록에서 대상을 호출 할 수 있다는 것입니다. 외부 사용자가 이벤트를 제기 할 때 액세스 할 수는 없습니다. - AddXXXHandler 및 RemoveXXXHandler 메소드가있는 델리게이트 변수
이 세 번째 옵션은 본질적으로 이벤트가 우리에게 제공하는 것입니다. EventHandler를 선언하면 속성이 아니라 공개적으로가 아니라 접근자를 추가 / 제거하는 이벤트를 대리자로 액세스 할 수 있습니다.
같은 프로그램이 어떻게 생겼는지 보지만 이제는 공개 대리자 대신 이벤트를 사용합니다 (중개자를 싱글 톤으로 변경했습니다).
예 2 : 공개 대리자 대신 EventHandler 사용
중재인:
class Mediator
{
private static readonly Mediator _Instance = new Mediator();
private Mediator() { }
public static Mediator GetInstance()
{
return _Instance;
}
public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate.
public void OnPersonChanged(object sender, Person p)
{
var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>;
if (personChangedDelegate != null)
{
personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
}
}
}
EventHandler에서 F12를 수행하면 정의가 추가 “보낸 사람”오브젝트가있는 일반화 된 대리자임을 표시합니다.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
사용자 컨트롤 :
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.GetInstance().PersonChanged += DetailView_PersonChanged;
}
void DetailView_PersonChanged(object sender, PersonChangedEventArgs e)
{
BindData(e.Person);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
마지막으로 Form1.cs 코드는 다음과 같습니다.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem);
}
EventHandler가 원하고 EventArgs를 매개 변수로 사용하기 때문에 단일 속성 만으로이 클래스를 만들었습니다.
class PersonChangedEventArgs
{
public Person Person { get; set; }
}
우리에게 이벤트가 왜 있는지, 그리고 이벤트가 델리게이트와는 다르지만 기능적으로는 어떻게 다른지에 대해 조금 보여 주길 바랍니다.
답변
델리게이트가 아닌 인터페이스 선언에서 이벤트를 사용할 수도 있습니다.
답변
이벤트와 대의원들 사이에 정말 큰 오해 !!! 델리게이트는 TYPE (예 : class
, 또는 do)을 지정 interface
하지만 이벤트는 일종의 MEMBER (예 : 필드, 속성 등)입니다. 그리고 다른 종류의 멤버와 마찬가지로 이벤트에도 유형이 있습니다. 그러나 이벤트의 경우 이벤트 유형은 대리인이 지정해야합니다. 예를 들어, 인터페이스에 의해 정의 된 유형의 이벤트를 선언 할 수 없습니다.
결론적으로, 우리는 다음과 같은 관찰을 할 수있다 : 이벤트의 타입은 반드시 delegate에 의해 정의되어야한다 . 이 이벤트와 대리인 간의 기본 관계이며 단원에서 설명 II.18 정의 이벤트 의 ECMA-335 (CLI)에 파티션 I VI :
일반적인 사용법에서 TypeSpec (있는 경우) 은 서명이 이벤트의 fire 메소드에 전달 된 인수와 일치 하는 대리자 를 식별합니다 .
그러나이 사실은 이벤트가 지원 위임 필드를 사용한다는 것을 의미하지는 않습니다 . 실제로, 이벤트는 선택한 다른 데이터 구조 유형의 백업 필드를 사용할 수 있습니다. C #에서 명시 적으로 이벤트를 구현하는 경우 이벤트 핸들러 를 저장하는 방법을 자유롭게 선택할 수 있습니다 ( 이벤트 핸들러 는 이벤트 유형의 인스턴스이며 , 이는 이전 관찰 의 위임 유형 임). ). 그러나 이러한 이벤트 처리기 (대리자 인스턴스)는 a 또는 a 와 같은 다른 데이터 구조 또는 백킹 대리자 필드에 저장할 수 있습니다 . 그러나 위임 필드를 반드시 사용해야하는 것은 아닙니다.List
Dictionary
