2016-05-13 19 views
1

Ich möchte in der Lage sein, eine Action<string> auf der App-Ebene anzugeben, die meine Bibliothek dann für die Fortschrittsberichterstattung verwenden könnte. ConfigurationManager.AppSettings erlaubt nur XmlSerializeable s und Action s sind das nicht.Wie kann ich eine einmalige Aktion auf App-Ebene erstellen, die nicht durch Laufzeitausnahmen oder das Verschlucken von Second-Sets erzwungen wird?

Die Motivation ist, dass Konsolen-Anwendungen möglicherweise nur auf die Konsole schreiben, Webapps vielleicht zu einer Ablaufverfolgung, und Formulare vielleicht zu Dateien oder einem bestimmten Feld, der Punkt ist die App sollte es imo konfigurieren können.

Mein Ansatz ist derzeit in der Bibliothek eine LibSettings Klasse, die eine statische Action<string> hat. Das bedeutet, dass jeder es auch anderswo einstellen kann, was potentiell für Fehler anfällig ist.

Zuerst dachte ich vielleicht, dass ein statischer Konstruktor (mit Parametern) funktionieren würde, aber es stellt sich heraus, dass man statische Konstruktoren nicht explizit aufrufen kann und man ihnen keine Parameter geben kann.

Gibt es eine Möglichkeit, mein Ziel zu erreichen, in der Lage zu sein, die Rückkopplungsaktion nur einmal in einer Art von benutzerdefinierten App-Einstellungen anzugeben und keine Runtime-Ausnahme bei der zweiten Einstellung zu werfen oder die zweite Einstellung zu schlucken? Das ist im Wesentlichen wie eine Singleton-Eigenschaft meines Designs, wenn ich es entwerfe. Danke im Voraus.

+0

Nun, erstellen Sie eine Eigenschaft und auf dem Setter überprüfen Sie einfach, ob das Backed-Feld Null ist, wenn es null ist, dann legen Sie es und wenn es nicht Null ist den Wert im Hintergrund zu verwerfen. – Gusman

+0

@Gusman: Tut mir leid, ich habe es gerade aktualisiert. Ich möchte auch nicht, dass es es verschluckt, denn das würde bedeuten, dass jemand diese Einstellung aktualisieren möchte und es einfach nicht getan hat. – user420667

+0

Hmmm, dann kann ich nicht verstehen, was du mit * mein Ziel meinst, in der Lage zu sein, die Feedback-Aktion einmal und nur in einer Art von benutzerdefinierten App-Einstellungen zu spezifizieren, und keine Laufzeitausnahme auf zweite Einstellung zu werfen * – Gusman

Antwort

1

Das Serialisieren und Deserialisieren eines Delegierten ist normalerweise keine gute Idee, da dies leicht zu ernsthaften Sicherheitsbedenken führt (siehe arbitrary code execution).

Stattdessen würde ich einen enum oder ähnlichen serialisierbaren Typ empfehlen, der eine Anzahl von statisch definierten Funktionen identifiziert und zwischen ihnen konvertiert. Etwas wie folgt aus:

public enum FeedbackAction 
{ 
    Console, 
    Trace, 
    ... 
} 

public static class FeedbackActions 
{ 
    public static void Console(string text) { ... } 
    public static void Trace(string text) { ... } 

    public static Action<string> GetAction(FeedbackAction action) 
    { 
     switch (action) 
     { 
      case FeedbackAction.Console: 
       return Console; 
      case FeedbackAction.Trace: 
       return Trace; 
      default: 
       throw new ArgumentException("Invalid feedback action.", nameof(action)); 
     } 
    } 
} 

Nun, wenn Sie versuchen, die App-Einstellung zu verwenden, nur FeedbackActions.GetAction rufen zwischen enum Werten und den entsprechenden Action<string> zu konvertieren.

Zum Beispiel:

public static class Feedback 
{ 
    public static Action<string> feedbackAction; 
    public static object syncLock = new object(); 

    public static void ProvideFeedback(string text) 
    { 
     if (feedbackAction == null) 
     { 
      // synchronize to avoid duplicate calls 
      lock (syncLock) 
      { 
       if (feedbackAction == null) 
       { 
        var value = ConfigurationManager.AppSettings["FeedbackAction"]; 
        feedbackAction = FeedbackActions.GetAction(value); 
       } 
      } 
     } 

     feedbackAction(text); 
    } 
} 

diese Weise können Sie sicher Feedback.ProvideFeedback nennen können, und sein Verhalten wird von der app/web.config Datei angesteuert werden.


Wenn Sie eine Lösung zu machen brauchen, die fast jede Rückkopplungswirkung zu behandeln flexibel genug ist, würde ich empfehlen, auf inversion of control im Allgemeinen zu lesen und die Managed Extensibility Framework (MEF) im Besonderen. Eine vollständige Umsetzung wäre ein bisschen zu komplex sein, hier zu bieten, aber im Allgemeinen wäre es ein bisschen wie folgt aussehen:

public interface IFeedbackAction 
{ 
    void ProvideFeedback(string text); 
} 

public interface IFeedbackMetadata 
{ 
    string Name { get; } 
} 

[Export(typeof(IFeedbackAction)), ExportMetadata("Name", "Console")] 
public interface ConsoleFeedbackAction : IFeedbackAction { ... } 

[Export(typeof(IFeedbackAction)), ExportMetadata("Name", "Trace")] 
public interface TraceFeedbackAction : IFeedbackAction { ... } 

public static class Feedback 
{ 
    [ImportMany] 
    public IEnumerable<Lazy<IFeedbackAction, IFeedbackMetadata>> FeedbackActions { get; set; } 

    private IFeedbackAction feedbackAction; 

    public static void ProvideFeedback(string text) 
    { 
     if (feedbackAction == null) 
     { 
      // synchronize to avoid duplicate calls 
      lock (syncLock) 
      { 
       if (feedbackAction == null) 
       { 
        var value = ConfigurationManager.AppSettings["FeedbackAction"]; 
        feedbackAction = GetFeedbackAction(value); 
       } 
      } 
     } 

     feedbackAction.ProvideFeedback(text); 
    } 

    private static IFeedbackAction GetFeedbackAction(string name) 
    { 
     return FeedbackActions 
      .First(l => l.Metadata.Name.Equals(name)).Value; 
    } 
} 

Mit dieser Methode die Verbraucher ihre eigene Implementierung von IFeedbackAction, verziert mit dem zur Verfügung zu stellen wäre in der Lage entsprechende [Export] und [ExportMetadata] Attribute, und geben Sie einfach die Verwendung ihrer benutzerdefinierten Aktionen in der Datei app/web.config an.

+0

Danke. Dies ist auch ein Ansatz, den ich in Betracht gezogen habe, aber es erlaubt mir nicht, wie FormTextBox.Text = s (in die Bibliothek) zu gehen, so dass meine Bibliothek nur mit X-Möglichkeiten umgehen könnte. Es ist eine gute Idee, aber danke. – user420667

+0

@ user420667 Es klingt, als ob Sie die Flexibilität benötigen, um mit arbiträren Implementierungen Ihrer Feedback-Aktion umgehen zu können. Dies klingt nach einem idealen Kandidaten für Inversion of Control (IoC). Siehe meine aktualisierte Antwort. –

+0

immer noch versuchen, Ihre aktualisierte Antwort zu verstehen. Ich habe schon vorher von IoC gehört, aber nie benutzt und sicherlich noch nie von der MEF gehört. Vielen Dank. – user420667

1

Ok, mal sehen, ob ich richtig verstanden habe.

Nehmen wir an, diese Config-Klasse ist:

public static class LibSettings 
{ 
    public static readonly Action<string> TheAction{ get; private set; } 

    static LibSettings() 
    { 
     var action = ConfigurationManager.AppSettings["libAction"]; 

     switch(action) 
     { 
      case "console": 

       TheAction = ConsoleAction; 
       break; 

      case "web": 

       TheAction = WebAction; 
       break; 

      //And as many as you need... 
     } 
    } 

    private static void ConsoleAction(string Parameter) 
    { 
     //Whatever it does... 
    } 

    private static void WebAction(string Parameter) 
    { 
     //Whatever it does... 
    } 

} 

Ist das, was Sie gemeint?Es wird nur einmal festgelegt, wenn Sie auf eine Eigenschaft der Klasse zugreifen, sie kann nicht extern geändert werden und ändert die Aktion für einen AppSet-Datensatz.

Ok, gehen wir mit einem anderen Ansatz. Jetzt haben wir zwei Klassen einen temporären Halter, wo Sie die gewünschte Aktion und die aktuelle Einstellungs-Klasse einstellen.

public static class TemporalHolder 
{ 
    public static Action<string> HeldAction{ get; set; } 
} 

public static class LibSettings 
{ 
    public static readonly Action<string> TheAction; 
    static LibSettings() 
    { 
     TheAction = TemporalHolder.HeldAction; 
    } 
    public static void Init() 
    { 
     /*Just do nothing as we will use it to fire the constructor*/ 
    } 
} 

Und nun, es zu benutzen, seth nur die Wirkung der zeitlichen Halter und rufen anithing statisch auf LibSettings:

TemporalHolder.Action = (your function); 
LibSettings.Init(); 

Und voila! keine Fehler bei den zweiten Einstellungen, kann zur Laufzeit nicht geändert werden und kann nicht neu zugeordnet werden. Sind alle Bedingungen erfüllt?

+0

Danke. Ja, das ist in etwa so, wie ich es möchte und ähnlich der Antwort von @p.s.w.g. Die einzige Sache ist, dass es das gleiche Problem hat, etwas FormTextField.Text = string nicht übergeben zu können. – user420667

+0

Okokoko, ich habe eine Idee, wie Sie genau das erreichen können, was Sie wollen, lassen Sie mich das aktualisieren: D. – Gusman

+0

Überprüfen Sie das Update;) – Gusman