¿Es posible definir una clase en C # tal que
class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate
No pude por mi vida lograr esto anoche en .NET 3.5. Intenté usar
delegate, Delegate, Action<T> and Func<T, T>
Me parece que esto debería estar permitido de alguna manera. Estoy tratando de implementar mi propio EventQueue.
Terminé haciendo esto [aproximación primitiva, fíjate].
internal delegate void DWork();
class EventQueue {
private Queue<DWork> eventq;
}
Pero luego pierdo la capacidad de reutilizar la misma definición para diferentes tipos de funciones.
Pensamientos
CA1065: Do not raise exceptions in unexpected locations
... Siempre asumí que debería usar una regla de análisis de código personalizado para encontrar usos no válidos de su clase que normalmente no están disponibles en tiempo de ejecución.where T : Delegate
, (y alguien publicó una nueva respuesta sobre eso a continuación).Sí, es posible en C # 7.3, las restricciones familiares aumentaron para incluir
Enum
,Delegate
yunmanaged
tipos. Puedes escribir este código sin problema:void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged { }
De Docs :
Enlaces útiles:
El futuro de C # , de Microsoft Build 2018
¿Qué hay de nuevo en C # 7.3?
fuente
Editar: En estos artículos se proponen algunas soluciones alternativas:
http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html
http://jacobcarpenters.blogspot.com/2006_11_01_archive.html
De la especificación C # 2.0 podemos leer (20.7, Restricciones):
Una restricción de tipo de clase debe satisfacer las siguientes reglas:
Y efectivamente VS2008 escupe un error:
error CS0702: Constraint cannot be special class 'System.Delegate'
Para obtener información e investigación sobre este problema, lea aquí .
fuente
Si está dispuesto a tomar una dependencia de tiempo de compilación en un IL Weaver, puede hacerlo con Fody .
Usando este complemento para Fody https://github.com/Fody/ExtraConstraints
Tu código puede verse así
public class Sample { public void MethodWithDelegateConstraint<[DelegateConstraint] T> () { } public void MethodWithEnumConstraint<[EnumConstraint] T>() { } }
Y ser compilado a esto
public class Sample { public void MethodWithDelegateConstraint<T>() where T: Delegate { } public void MethodWithEnumConstraint<T>() where T: struct, Enum { } }
fuente
El delegado ya admite el encadenamiento. ¿No satisface esto sus necesidades?
public class EventQueueTests { public void Test1() { Action myAction = () => Console.WriteLine("foo"); myAction += () => Console.WriteLine("bar"); myAction(); //foo //bar } public void Test2() { Action<int> myAction = x => Console.WriteLine("foo {0}", x); myAction += x => Console.WriteLine("bar {0}", x); myAction(3); //foo 3 //bar 3 } public void Test3() { Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; }; myFunc += x => { Console.WriteLine("bar {0}", x); return x + 1; }; int y = myFunc(3); Console.WriteLine(y); //foo 3 //bar 3 //4 } public void Test4() { Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; }; Func<int, int> myNextFunc = x => { x = myFunc(x); Console.WriteLine("bar {0}", x); return x + 1; }; int y = myNextFunc(3); Console.WriteLine(y); //foo 3 //bar 5 //6 } }
fuente
Me encontré con una situación en la que necesitaba lidiar con una
Delegate
restricción interna pero quería una restricción genérica. Específicamente, quería agregar un controlador de eventos usando la reflexión, pero quería usar un argumento genérico para el delegado. El siguiente código NO funciona, ya que "Handler" es una variable de tipo y el compilador no se convierteHandler
enDelegate
:public void AddHandler<Handler>(Control c, string eventName, Handler d) { c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d); }
Sin embargo, puede pasar una función que realice la conversión por usted.
convert
toma unHandler
argumento y devuelve unDelegate
:public void AddHandler<Handler>(Control c, string eventName, Func<Delegate, Handler> convert, Handler d) { c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d)); }
Ahora el compilador está contento. Llamar al método es fácil. Por ejemplo, adjuntar al
KeyPress
evento en un control de Windows Forms:AddHandler<KeyEventHandler>(someControl, "KeyPress", (h) => (KeyEventHandler) h, SomeControl_KeyPress);
¿Dónde
SomeControl_KeyPress
está el objetivo del evento? La clave es el convertidor lambda: no funciona, pero convence al compilador de que le dio un delegado válido.(Comienza 280Z28) @Justin: ¿Por qué no usar esto?
public void AddHandler<Handler>(Control c, string eventName, Handler d) { c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate); }
(Fin 280Z28)
fuente
Como se mencionó anteriormente, no puede tener Delegates y Enum como una restricción genérica.
System.Object
ySystem.ValueType
tampoco se puede utilizar como una restricción genérica.La solución puede ser si construye una llamada adecuada en su IL. Funcionará bien.
Aquí hay un buen ejemplo de Jon Skeet.
http://code.google.com/p/unconstrained-melody/
He tomado mis referencias del libro C # in Depth de Jon Skeet , tercera edición.
fuente
Según MSDN
Error del compilador CS0702
La restricción no puede ser un 'identificador' de clase especial. Los siguientes tipos no pueden usarse como restricciones:
fuente