C # genéricos não vai permitir Delegado Tipo Restrições
-
08-07-2019 - |
Pergunta
É possível definir uma classe em C # tal que
class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate
Eu poderia não para a vida de mim realizar esta última noite em .NET 3.5. Eu tentei usar
delegate, Delegate, Action<T> and Func<T, T>
Parece-me que este deve ser permitido de alguma forma. Eu estou tentando implementar meu próprio EventQueue.
I terminou apenas fazendo isso [aproximação mente primitiva você].
internal delegate void DWork();
class EventQueue {
private Queue<DWork> eventq;
}
Mas então eu perder a capacidade de reutilizar a mesma definição para diferentes tipos de funções.
Os pensamentos?
Solução
Um número de classes não estão disponíveis como genéricos contraints -. Enum ser outro
Para os delegados, o mais próximo que você pode obter é ": classe", talvez usando a reflexão para verificar (por exemplo, no construtor estático) que a T é um delegado:
static GenericCollection()
{
if (!typeof(T).IsSubclassOf(typeof(Delegate)))
{
throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
}
}
Outras dicas
Editar: Algumas soluções alternativas propostas são propostas nestes artigos:
http://jacobcarpenters.blogspot.com/ 2006/06 / c-30-e-delegado-conversion.html
http://jacobcarpenters.blogspot.com/2006_11_01_archive.html
do C # 2.0 especificação podemos ler (20.7, Restrições):
A-tipo de classe restrição deve satisfazer as seguintes regras:
- O tipo deve ser um tipo de classe.
- O tipo não deve ser selado.
- O tipo não deve ser um dos seguintes tipos:. System.Array, System.Delegate, System.Enum, ou System.ValueType
- O tipo não deve ser objeto. Porque todos os tipos derivam do objeto, tal restrição não teria nenhum efeito se fosse permitido.
- No máximo um constrangimento para um determinado tipo de parâmetro pode ser um tipo de classe.
E com certeza VS2008 cospe um erro:
error CS0702: Constraint cannot be special class 'System.Delegate'
Para informações e investigação sobre este assunto leia aqui .
Se você está disposto a assumir uma dependência em tempo de compilação em um IL Weaver que você pode fazer isso com Fody .
Usando este suplemento para Fody https://github.com/Fody/ExtraConstraints
Seu código pode parecer com isto
public class Sample
{
public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{
}
public void MethodWithEnumConstraint<[EnumConstraint] T>()
{
}
}
E ser compilado para este
public class Sample
{
public void MethodWithDelegateConstraint<T>() where T: Delegate
{
}
public void MethodWithEnumConstraint<T>() where T: struct, Enum
{
}
}
Sim, é possível em C # 7.3, Restrições de família aumentou para incluir Enum
, Delegate
e tipos unmanaged
.
Você pode escrever o código sem um problema:
void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged
{
}
Links úteis:
O futuro do C # , do Microsoft Build 2018
Delegado já suporta encadeamento. Isso não atender às suas necessidades?
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
}
}
me deparei com uma situação em que eu precisava lidar com uma Delegate
internamente, mas eu queria uma restrição genérica. Especificamente, eu queria adicionar um manipulador de eventos usando a reflexão, mas eu queria usar um argumento genérico para o delegado. O código a seguir não funciona, uma vez que "Handler" é uma variável do tipo, eo compilador não rejeitará Handler
para Delegate
:
public void AddHandler<Handler>(Control c, string eventName, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
}
No entanto, você pode passar uma função que faz a conversão para você. convert
leva um argumento Handler
e retorna um Delegate
:
public void AddHandler<Handler>(Control c, string eventName,
Func<Delegate, Handler> convert, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d));
}
Agora, o compilador é feliz. Chamar o método é fácil. Por exemplo, anexando ao evento KeyPress
em um controle Windows Forms:
AddHandler<KeyEventHandler>(someControl,
"KeyPress",
(h) => (KeyEventHandler) h,
SomeControl_KeyPress);
onde SomeControl_KeyPress
é o destino do evento. A chave é o lambda conversor -. Não faz nenhum trabalho, mas convence o compilador que você deu um delegado válido
(Comece 280Z28) @Justin:? Por que não usar este
public void AddHandler<Handler>(Control c, string eventName, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate);
}
(Fim 280Z28)
Como mencionado acima, você não pode ter Delegados e Enum como uma restrição genérica. System.Object
e System.ValueType
também não pode ser usado como uma restrição genérica.
A em torno do trabalho pode ser se você construir uma chamada apropriada em você IL. Ele vai funcionar bem.
Aqui está um bom exemplo por Jon Skeet.
http://code.google.com/p/unconstrained-melody/
Tomei minhas referências do livro de Jon Skeet C # in Depth , 3ª edição.
De acordo com a MSDN
Compiler erro CS0702
restrição não pode ser classe 'identificador' especial os seguintes tipos não podem ser utilizados como restrições:
- System.Object
- System.Array
- System.Delegate
- System.Enum
- System.ValueType.