29 março 2008

Utilização de Delegates em Genéricos

A minha primeira experiência com genéricos foi em C++, eram simplesmente brutais, autênticas macros, davam para fazer coisas simplesmente espectaculares. Eu sei que eram particularmente perigosos, obrigavam a que o programador soubesse o que estava a fazer.

Comigo geralmente funcionavam bem desde que tivesse sido eu a fazê-los, quando usava bibliotecas de genéricos era de facto muito mais complicado. Como tal não fiquei muito surpreendido por a Microsoft os ter simplificado em muito, mas confesso que foi uma verdadeira desilusão.

Ainda assim, volta e meia tento aprofundar as limitações que encontro, e mesmo quando não as encontro, é frequente quando volto a mexer nesse código tentar novamente.

E foi precisamente isso que hoje me aconteceu, peguei num pedaço de código já bem antigo, do tempo em que os genéricos chegaram ao mono:

public delegate void Event(T changed);

public abstract class A<T>
{
  public void Fire(Event aux)
  {
    aux(this);
  }
}
public class B : A<B>
{
}
public class C
{
  public static void Main(string[] args)
  {
    B b = new B();
    b.Fire(Yo);
  }
  public static void Yo(A<B> b)
  {
    System.Console.WriteLine("Yo");
  }
}

O que eu pretendia era que o Delegate Yo recebesse B e não A<B>, para além de complicar o código, implica estar dependente da hierarquia. Não sei porque motivo me escapou na altura, talvez algum bug do mono, dado que na altura o suporte era muito preliminar. Mas hoje tentei novamente e consegui:

public delegate void Event(T changed);

public abstract class A<T> where T : A<T>
{
  public void Fire(Event aux)
  {
    aux((T)this);
  }
}
public class B : AA<B>
{
}
public class C
{
  public static void Main(string[] args)
  {
    B b = new B();
    b.Fire(Yo);
  }
  public static void Yo(B b)
  {
    System.Console.WriteLine("Yo");
  }
}

Supostamente bastaria meter where T : A, mas não, tive de fazer um cast do this para T.
A classe é abstract, o método não é static, logo qualquer instância tem de ser obrigatoriamente T. Já que o compilador não consegue ver isso, espero que o JIT consiga.

Sem comentários: