Est-il possible de définir une classe en C # telle que
class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate
Je ne pourrais pas pour la vie de moi accomplir cette dernière nuit dans .NET 3.5. J'ai essayé d'utiliser
delegate, Delegate, Action<T> and Func<T, T>
Il me semble que cela devrait être permis d'une manière ou d'une autre. J'essaie d'implémenter mon propre EventQueue.
J'ai fini par faire cette [approximation primitive, pensez-vous].
internal delegate void DWork();
class EventQueue {
private Queue<DWork> eventq;
}
Mais je perds ensuite la possibilité de réutiliser la même définition pour différents types de fonctions.
Pensées?
Un certain nombre de classes ne sont pas disponibles en tant que contraintes génériques - Enum étant une autre.
Pour les délégués, le plus proche que vous pouvez obtenir est ": class", peut-être en utilisant la réflexion pour vérifier (par exemple, dans le constructeur statique) que le T is un délégué:
static GenericCollection()
{
if (!typeof(T).IsSubclassOf(typeof(Delegate)))
{
throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
}
}
Modifier: Certaines solutions de contournement proposées sont proposées dans ces articles:
http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html
http://jacobcarpenters.blogspot.com/2006_11_01_archive.html
D'après la spécification C # 2. nous pouvons lire (20.7, Contraintes):
Une contrainte de type classe doit satisfaire aux règles suivantes:
Et bien sûr, VS2008 crache une erreur:
error CS0702: Constraint cannot be special class 'System.Delegate'
Pour plus d'informations et une enquête sur ce problème, lisez ici .
Si vous êtes prêt à prendre une dépendance de temps de compilation sur un IL Weaver, vous pouvez le faire avec Fody .
Utiliser cet addin pour Fody https://github.com/Fody/ExtraConstraints
Votre code peut ressembler à ceci
public class Sample
{
public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{
}
public void MethodWithEnumConstraint<[EnumConstraint] T>()
{
}
}
Et être compilé pour cela
public class Sample
{
public void MethodWithDelegateConstraint<T>() where T: Delegate
{
}
public void MethodWithEnumConstraint<T>() where T: struct, Enum
{
}
}
Oui, c'est possible en C # 7.3, la famille des contraintes a été augmentée pour inclure les types Enum
, Delegate
et unmanaged
. Vous pouvez écrire ce code sans problème:
void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged
{
}
Liens utiles:
L'avenir de C # , de Microsoft Build 2018
Le délégué soutient déjà le chaînage. Cela ne répond-il pas à vos besoins?
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
}
}
Je suis tombé sur une situation où je devais gérer un Delegate
en interne mais je voulais une contrainte générique. Plus précisément, je voulais ajouter un gestionnaire d'événements à l'aide de la réflexion, mais je voulais utiliser un argument générique pour le délégué. Le code ci-dessous ne fonctionne PAS, car "Handler" est une variable de type et le compilateur ne transtypera pas Handler
en Delegate
:
public void AddHandler<Handler>(Control c, string eventName, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
}
Cependant, vous pouvez transmettre une fonction qui effectue la conversion pour vous. convert
prend un argument Handler
et renvoie un Delegate
:
public void AddHandler<Handler>(Control c, string eventName,
Func<Delegate, Handler> convert, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d));
}
Maintenant, le compilateur est content. Appeler la méthode est simple. Par exemple, l'attachement à l'événement KeyPress
sur un contrôle Windows Forms:
AddHandler<KeyEventHandler>(someControl,
"KeyPress",
(h) => (KeyEventHandler) h,
SomeControl_KeyPress);
où SomeControl_KeyPress
est la cible de l'événement. La clé est le convertisseur lambda - il ne fonctionne pas, mais il convainc le compilateur que vous lui avez donné un délégué valide.
(Begin 280Z28) @Justin: Pourquoi ne pas l'utiliser?
public void AddHandler<Handler>(Control c, string eventName, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate);
}
(Fin 280Z28)
Comme mentionné ci-dessus, vous ne pouvez pas avoir Délégués et Enum comme contrainte générique. System.Object
et System.ValueType
ne peut pas non plus être utilisé comme contrainte générique.
Le travail peut être si vous construisez un appel approprié dans votre IL. Cela fonctionnera bien.
Voici un bon exemple de Jon Skeet.
http://code.google.com/p/unconstrained-melody/
J'ai pris mes références dans le livre de Jon Skeet C # in Depth , 3rd edition.
Selon MSDN
Erreur du compilateur CS0702
La contrainte ne peut pas être une classe spéciale 'identificateur' Les types suivants ne peuvent pas être utilisés comme contraintes: