Prima di tutto, Unity supporta un sottoinsieme di .NET 3.5 in cui il sottoinsieme specifico dipende dai parametri di costruzione.
Passando alla domanda, il modello di evento generale in C# utilizza i delegati e la parola chiave dell'evento. Poiché si desidera chiamare i gestori solo se il frutto in entrata è compatibile con la definizione del metodo, è possibile utilizzare un dizionario per eseguire la ricerca. Il trucco è di che tipo memorizzare i delegati come. È possibile utilizzare un po 'tipo di magia per farlo funzionare e memorizzare tutto come
Dictionary<Type, Action<Fruit>> handlers = new Dictionary<Type, Action<Fruit>>();
Questo non è l'ideale, perché ora tutti i gestori sembrano accettare Fruit
invece dei tipi più specifici. Questa è solo la rappresentazione interna, tuttavia, le persone pubblicamente aggiungeranno ancora gestori specifici tramite
public void RegisterHandler<T>(Action<T> handler) where T : Fruit
Ciò mantiene l'API pubblica pulita e specifica del tipo.Internamente il delegato deve passare da Action<T>
a Action<Fruit>
. Per fare ciò, crea un nuovo delegato che acquisisca un Fruit
e lo trasformi in un T
.
Action<Fruit> wrapper = fruit => handler(fruit as T);
Questo non è certo un cast sicuro. Si bloccherà se viene passato qualcosa che non è T
(o eredita da T
). Questo è il motivo per cui è molto importante archiviare solo internamente e non esposto al di fuori della classe. Memorizza questa funzione sotto la chiave Type
typeof(T)
nel dizionario dei gestori.
Successivamente per richiamare l'evento è necessaria una funzione personalizzata. Questa funzione deve richiamare tutti i gestori di eventi dal tipo dell'argomento lungo tutta la catena di ereditarietà ai più generici gestori di Fruit
. Ciò consente a una funzione di essere trigger su qualsiasi argomento sottotipo, non solo sul suo tipo specifico. Questo mi sembra il comportamento intuitivo, ma può essere lasciato fuori se lo si desidera.
Infine, è possibile visualizzare un evento normale per consentire l'aggiunta di tutti gli operatori di tipo Fruit
nel solito modo.
Di seguito è riportato l'esempio completo. Si noti che l'esempio è abbastanza minimale ed esclude alcuni controlli di sicurezza tipici come il controllo nullo. Esiste anche un potenziale loop infinito se non esiste una catena di ereditarietà da child
a parent
. Un'attuazione effettiva dovrebbe essere ampliata come si ritiene opportuno. Potrebbe anche utilizzare alcune ottimizzazioni. Soprattutto negli scenari ad alto utilizzo la memorizzazione nella cache delle catene ereditarie potrebbe essere importante.
public class Fruit { }
class FruitHandlers
{
private Dictionary<Type, Action<Fruit>> handlers = new Dictionary<Type, Action<Fruit>>();
public event Action<Fruit> FruitAdded
{
add
{
handlers[typeof(Fruit)] += value;
}
remove
{
handlers[typeof(Fruit)] -= value;
}
}
public FruitHandlers()
{
handlers = new Dictionary<Type, Action<Fruit>>();
handlers.Add(typeof(Fruit), null);
}
static IEnumerable<Type> GetInheritanceChain(Type child, Type parent)
{
for (Type type = child; type != parent; type = type.BaseType)
{
yield return type;
}
yield return parent;
}
public void RegisterHandler<T>(Action<T> handler) where T : Fruit
{
Type type = typeof(T);
Action<Fruit> wrapper = fruit => handler(fruit as T);
if (handlers.ContainsKey(type))
{
handlers[type] += wrapper;
}
else
{
handlers.Add(type, wrapper);
}
}
private void InvokeFruitAdded(Fruit fruit)
{
foreach (var type in GetInheritanceChain(fruit.GetType(), typeof(Fruit)))
{
if (handlers.ContainsKey(type) && handlers[type] != null)
{
handlers[type].Invoke(fruit);
}
}
}
}
si potrebbe fare Frutta generico - '' Frutta –
@ DanielA.White io non sono sicuro di vedere come sarebbe affrontarlo. Potresti elaborare? – thomas88wp
Ho modificato il tuo titolo. Per favore vedi, "[Le domande dovrebbero includere" tag "nei loro titoli?] (Http://meta.stackexchange.com/questions/19190/)", dove il consenso è "no, non dovrebbero". –