Le biblioteche devono utilizzare eventi o un'azione impostata

4

Sto costruendo una piccola libreria riutilizzabile per due sistemi gestiti dalla nostra azienda.

Qualcosa su cui sono stato coinvolto è se dovrei esporre un set di proprietà di tipo Action<T> per eventi come Completato, Interrotto, EccezioneOccurato, o se dovrei usare la metodologia Event<T> T: EventArgs.

In pratica un'altra libreria interna consumerà queste informazioni e gestirà la visualizzazione delle informazioni all'utente. Le classi della libreria verranno istanziate all'interno di un singolo thread e non verranno utilizzate da elementi esterni al proprietario.

Per me sembra più sulla falsariga di se solo le notifiche debbano essere solo consumate da un oggetto, ma non importa, comunque per completezza, e perché io come biblioteca avere un controllo limitato su come un altro sviluppatore implementa questo, dovrei usare il metodo Event a causa della sua più ampia capacità di gestire gli eventi.

EDIT (codice di esempio):

@Robert Harvey, ho letto quel post durante la ricerca del ragionamento sul picking uno sull'altro. Capisco la risposta nel contesto di quella domanda, ma non sono sicuro di come si riferisce all'uso della parola chiave dell'evento o alla semplice assegnazione di una proprietà.

@Fabio Marcolini, capisco che sto usando la parola evento per descrivere cosa sta succedendo. Quello a cui sto cercando di rispondere è che se si utilizza la struttura degli eventi è preferibile semplicemente eseguire una proprietà delegata.

Ecco le proprietà che uso per definire le azioni dell'evento:

public Action<WorkflowInstallerCompletedEventArgs> InstallCompleted;

public Action<System.Activities.Activity, WorkflowInstallerActivityCompletedEventArgs> ActivityCompleted;

public Action<System.Activities.Activity, WorkflowApplicationAbortedEventArgs> InstallAborted;

public Func<System.Activities.Activity, WorkflowApplicationUnhandledExceptionEventArgs, UnhandledExceptionAction> UnhandledExceptionDuringInstall;

L'idea sarebbe quella di invocare uno di questi in questo modo:

  if (this.InstallCompleted != null)
    this.InstallCompleted(new WorkflowInstallerCompletedEventArgs(logs));

Ora fino al commento di @ svick avrei detto che si poteva avere solo un'azione (o Func) assegnata a ciascuna proprietà.

Implementazione di esempio:

  WorkflowInstaller installer = new WorkflowInstaller(activity, new TestVariablesCollection()) { ActivitiesRunSequentially = true };
  installer.AddInstallActivity(activity2);

  installer.InstallCompleted = (e) =>
  {
    Log[] logs = e.Logs.ToArray();
    string test = SerializationHelper<Log>.Serialize(logs.FirstOrDefault());
    try
    {
      logOutputFromRun = SerializationHelper<Log[]>.Serialize(logs);
    }
    catch (Exception ex)
    {

    }
    syncEvent.Set();
  };
  installer.InstallAborted = (a, e) =>
  {
    Assert.Fail("Unexpected abort during the install.");
    syncEvent.Set();
  };
  installer.UnhandledExceptionDuringInstall = (a, e) =>
  {
    Assert.Fail("Unexpected exception during the install.");
    syncEvent.Set();
    return System.Activities.UnhandledExceptionAction.Terminate;
  };

  installer.StartInstallation();

  syncEvent.WaitOne();

La risposta alla mia domanda potrebbe non essere definitiva in questo modo o in quel modo, ma più di best practice o approccio generalmente accettato. Sto davvero cercando di ampliare i miei orizzonti quando si tratta di creare codice che sia accettabile per un pubblico più ampio.

    
posta Mike G 07.03.2014 - 16:19
fonte

1 risposta

4

Se è veramente pensato per essere una libreria, allora dovresti usare Eventi. Per i motivi che Fabio ha menzionato (la nomenclatura e l'esame delle librerie .NET esistenti), nonché la pratica che i consumatori non dovrebbero conoscere o preoccuparsi l'un l'altro.

Che cosa intendo con quest'ultima affermazione? Bene, considera il caso seguente, in cui stai utilizzando un'istanza condivisa di WorkflowInstaller installer :

La Classe 1 vuole implementare il tuo evento / azione come Azione:

installer.InstallCompleted = (e) => { /* do something */ );

Ora, la Classe 2 vuole anche implementare il tuo evento / azione come Azione:

installer.InstallCompleted = (e) => { /* do something else */ );

Bene, ora la Classe 2 ha appena sovrascritto il gestore della Classe 1. L'azione dalla Classe 1 non verrà mai chiamata, causando un comportamento inaspettato.

Implementato come evento, tuttavia, sarebbe invece:

La classe 1 implementa l'evento:

installer.InstallCompleted += (e) => { /* do something */ };

La classe 2 implementa l'evento:

installer.InstallCompleted += (e) => { /* do something else */ };

Ora, naturalmente, possiamo aggirare la sovrascrittura nel caso Azione controllando se qualche implementazione è già in atto:

La classe 2 implementa l'azione:

if (installer.InstallCompleted == null)
{
    installer.InstallCompleted = (e) => { /* do something else */ };
}
else
{
    var existingAction = installer.InstallCompleted;
    installer.InstallCompleted = (e) => { existingAction(e); /* do something else */ };
}

In tal modo, possiamo aggirarlo, ma costringiamo i consumatori a essere consapevoli di altri consumatori. Stiamo compiendo uno sforzo di codifica più ampio su chiunque altro. Questo è in un certo senso contrario alla ragione per creare una libreria in primo luogo.

In una nota finale, con tutto quanto sopra detto, non è particolarmente importante nel tuo caso, come hai detto che sia la biblioteca che i consumatori saranno interni al tuo spazio di lavoro. Tuttavia, come hai già detto, nell'interesse della completezza, consiglierei eventi.

    
risposta data 15.03.2014 - 16:33
fonte

Leggi altre domande sui tag