En primer lugar, buena pregunta. Admiro su enfoque en la utilidad en lugar de aceptar ciegamente las "mejores prácticas". +1 por eso.
He leído esa guía antes. Debe recordar algo al respecto: es solo una guía, principalmente para los recién llegados a C # que saben programar pero no están tan familiarizados con la forma en que C # hace las cosas. No es tanto como una página de reglas, sino que es una página que describe cómo se suelen hacer las cosas. Y dado que ya se han hecho de esta manera en todas partes, puede ser una buena idea mantenerse constante.
Iré al grano y responderé tus preguntas.
En primer lugar, supongo que ya sabes qué es una interfaz. En cuanto a un delegado, es suficiente decir que es una estructura que contiene un puntero escrito a un método, junto con un puntero opcional al objeto que representa el this
argumento para ese método. En el caso de métodos estáticos, el último puntero es nulo.
También hay delegados de multidifusión, que son como los delegados, pero pueden tener varias de estas estructuras asignadas a ellos (lo que significa que una sola llamada a Invocar en un delegado de multidifusión invoca todos los métodos en su lista de invocación asignada).
¿Qué quieren decir con un patrón de diseño de eventos?
Significan usar eventos en C # (que tiene palabras clave especiales para implementar sofisticadamente este patrón extremadamente útil). Los eventos en C # son alimentados por delegados de multidifusión.
Cuando define un evento, como en este ejemplo:
class MyClass {
// Note: EventHandler is just a multicast delegate,
// that returns void and accepts (object sender, EventArgs e)!
public event EventHandler MyEvent;
public void DoSomethingThatTriggersMyEvent() {
// ... some code
var handler = MyEvent;
if (handler != null)
handler(this, EventArgs.Empty);
// ... some other code
}
}
El compilador realmente transforma esto en el siguiente código:
class MyClass {
private EventHandler MyEvent = null;
public void add_MyEvent(EventHandler value) {
MyEvent += value;
}
public void remove_MyEvent(EventHandler value) {
MyEvent -= value;
}
public void DoSomethingThatTriggersMyEvent() {
// ... some code
var handler = MyEvent;
if (handler != null)
handler(this, EventArgs.Empty);
// ... some other code
}
}
Luego se suscribe a un evento haciendo
MyClass instance = new MyClass();
instance.MyEvent += SomeMethodInMyClass;
Que se compila a
MyClass instance = new MyClass();
instance.add_MyEvent(new EventHandler(SomeMethodInMyClass));
Entonces eso está sucediendo en C # (o .NET en general).
¿Cómo resulta fácil la composición si se usa un delegado?
Esto se puede demostrar fácilmente:
Supongamos que tiene una clase que depende de un conjunto de acciones que se le pasarán. Puede encapsular esas acciones en una interfaz:
interface RequiredMethods {
void DoX();
int DoY();
};
Y cualquiera que quisiera pasar acciones a su clase primero tendría que implementar esa interfaz. O podría facilitarles la vida dependiendo de la siguiente clase:
sealed class RequiredMethods {
public Action DoX;
public Func<int> DoY();
}
De esta forma, las personas que llaman solo tienen que crear una instancia de RequiredMethods y vincular métodos a los delegados en tiempo de ejecución. Esto suele ser más fácil.
Esta forma de hacer las cosas es extremadamente beneficiosa en las circunstancias correctas. Piénselo: ¿por qué depender de una interfaz cuando lo único que realmente le importa es que le pasen una implementación?
Beneficios de usar interfaces cuando hay un grupo de métodos relacionados
Es beneficioso usar interfaces porque las interfaces normalmente requieren implementaciones explícitas en tiempo de compilación. Esto significa que crea una nueva clase.
Y si tiene un grupo de métodos relacionados en un solo paquete, es beneficioso que ese paquete sea reutilizable por otras partes del código. Entonces, si pueden simplemente crear una instancia de una clase en lugar de crear un conjunto de delegados, es más fácil.
Beneficios de usar interfaces si una clase solo necesita una implementación
Como se señaló anteriormente, las interfaces se implementan en tiempo de compilación, lo que significa que son más eficientes que invocar a un delegado (que es un nivel de indirección per se).
"Una implementación" podría significar una implementación que existe en un solo lugar bien definido.
De lo contrario, una implementación podría provenir de cualquier parte del programa que simplemente se ajuste a la firma del método. Eso permite una mayor flexibilidad, porque los métodos solo necesitan ajustarse a la firma esperada, en lugar de pertenecer a una clase que implemente explícitamente una interfaz específica. Pero esa flexibilidad puede tener un costo y, de hecho, rompe el principio de sustitución de Liskov , porque la mayoría de las veces desea ser explícito, ya que minimiza la posibilidad de accidentes. Al igual que la escritura estática.
El término también podría referirse a los delegados de multidifusión aquí. Los métodos declarados por las interfaces solo se pueden implementar una vez en una clase de implementación. Pero los delegados pueden acumular múltiples métodos, que se llamarán secuencialmente.
Así que, en general, parece que la guía no es lo suficientemente informativa y simplemente funciona como lo que es: una guía, no un libro de reglas. Algunos consejos pueden sonar un poco contradictorios. Depende de usted decidir cuándo es correcto aplicar qué. La guía parece solo darnos un camino general.
Espero que sus preguntas hayan sido respondidas a su satisfacción. Y de nuevo, felicitaciones por la pregunta.