Domanda

È possibile definire una classe in C # tale che

class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate

Non sono riuscito per tutta la vita a realizzare quest'ultima notte in .NET 3.5. Ho provato a usare

delegate, Delegate, Action<T> and Func<T, T>

Mi sembra che ciò dovrebbe essere consentito in qualche modo. Sto cercando di implementare il mio EventQueue.

Ho finito per fare questa [approssimazione primitiva, intendiamoci].

internal delegate void DWork();

class EventQueue {
    private Queue<DWork> eventq;
}

Ma poi perdo la possibilità di riutilizzare la stessa definizione per diversi tipi di funzioni.

Pensieri?

È stato utile?

Soluzione

Un numero di classi non è disponibile come contraffazione generica - Enum è un'altra.

Per i delegati, il più vicino che puoi ottenere è " ;: class " forse usando la riflessione per verificare (ad esempio, nel costruttore statico) che T sia un delegato:

static GenericCollection()
{
    if (!typeof(T).IsSubclassOf(typeof(Delegate)))
    {
        throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
    }
}

Altri suggerimenti

Modifica: alcuni rimedi proposti sono proposti in questi articoli:

http://jacobcarpenters.blogspot.com/ 2006/06 / c-30-e-delegato-conversion.html

http://jacobcarpenters.blogspot.com/2006_11_01_archive.html


Dal Specifica C # 2.0 possiamo leggere (20.7, Vincoli):

Un vincolo di tipo classe deve soddisfare le seguenti regole:

  • Il tipo deve essere un tipo di classe.
  • Il tipo non deve essere sigillato.
  • Il tipo non deve essere uno dei seguenti tipi: System.Array, System.Delegate, System.Enum o System.ValueType .
  • Il tipo non deve essere oggetto. Poiché tutti i tipi derivano dall'oggetto, un tale vincolo non avrebbe alcun effetto se fosse consentito.
  • Al massimo un vincolo per un dato parametro di tipo può essere un tipo di classe.

E sicuramente VS2008 emette un errore:

error CS0702: Constraint cannot be special class 'System.Delegate'

Per informazioni e approfondimenti su questo argomento leggi qui .

Se sei disposto a prendere una dipendenza del tempo di compilazione da un tessitore IL, puoi farlo con Fody .

Utilizzo di questo componente aggiuntivo per Fody https://github.com/Fody/ExtraConstraints

Il tuo codice può apparire così

public class Sample
{
    public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
    {        
    }
    public void MethodWithEnumConstraint<[EnumConstraint] T>()
    {
    }
} 

Ed essere compilato in questo

public class Sample
{
    public void MethodWithDelegateConstraint<T>() where T: Delegate
    {
    }

    public void MethodWithEnumConstraint<T>() where T: struct, Enum
    {
    }
}

Sì, è possibile in C # 7.3, la famiglia di vincoli è stata aumentata per includere i tipi Enum, Delegate e unmanaged. Puoi scrivere questo codice senza problemi:

void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged
    {

    }

Link utili:

Il futuro di C # , da Microsoft Build 2018

Novità in C # 7.3?

Il delegato supporta già il concatenamento. Questo non soddisfa le tue esigenze?

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
    }

}

Mi sono imbattuto in una situazione in cui dovevo affrontare un Delegate internamente ma volevo un vincolo generico. In particolare, volevo aggiungere un gestore di eventi usando reflection, ma volevo usare un argomento generico per il delegato. Il codice seguente NON funziona, poiché & Quot; Gestore & Quot; è una variabile di tipo e il compilatore non eseguirà il cast da Handler a convert:

public void AddHandler<Handler>(Control c, string eventName, Handler d) {
  c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
}

Tuttavia, puoi passare una funzione che esegue la conversione per te. KeyPress accetta un SomeControl_KeyPress argomento e restituisce un <=>:

public void AddHandler<Handler>(Control c, string eventName, 
                  Func<Delegate, Handler> convert, Handler d) {
      c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d));
}

Ora il compilatore è felice. Chiamare il metodo è semplice. Ad esempio, allegando all'evento <=> su un controllo Windows Form:

AddHandler<KeyEventHandler>(someControl, 
           "KeyPress", 
           (h) => (KeyEventHandler) h,
           SomeControl_KeyPress);

dove <=> è il target dell'evento. La chiave è il convertitore lambda: non funziona, ma convince il compilatore che gli hai assegnato un delegato valido.

(Inizia 280Z28) @Justin: Perché non usarlo?

public void AddHandler<Handler>(Control c, string eventName, Handler d) { 
  c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate); 
} 

(Fine 280Z28)

Come menzionato sopra, non puoi avere Delegati ed Enum come vincolo generico. Anche System.Object e System.ValueType non possono essere utilizzati come vincolo generico.

Il problema può essere se si costruisce una chiamata appropriata in IL. Funzionerà benissimo.

Ecco un buon esempio di Jon Skeet.

http://code.google.com/p/unconstrained-melody/

Ho preso i miei riferimenti dal libro di Jon Skeet C # in profondità , terza edizione.

Secondo MSDN

Errore del compilatore CS0702

Il vincolo non può essere un 'identificatore' di classe speciale I seguenti tipi non possono essere usati come vincoli:

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top