2009-06-29 8 views
5

lassen Sie uns sagen, ich habeGibt es eine Möglichkeit, eine Methode zu erzwingen, bestimmte Methodensignatur zu folgen?

public delegate DataSet AutoCompleteDelegate(
     string filter, long rowOffset); 

kann ich die folgende Klasse machen diese Methode Signatur zu erzwingen? (Nur beschworene Idee):

public class MiddleTier 
{ 
    [Follow(AutoCompleteDelegate)] 
    public DataSet Customer_AutoComplete(string filter, long rowOffset) 
    { 
     var c = Connect(); 
     // some code here 
    } 

    [Follow(AutoCompleteDelegate)] 
    public DataSet Item_AutoComplete(string filter, long rowOffset) 
    { 
     var c = Connect(); 
     // some code here 
    } 



    // this should give compilation error, doesn't follow method signature 
    [Follow(AutoCompleteDelegate)] 
    public DataSet BranchOffice_AutoComplete(string filter, string rowOffset) 
    { 
     var c = Connect(); 
     // some code here 
    }   

} 

[EDIT]

Zweck: Ich habe bereits Attribute in meinem Middle Methoden. Ich habe Methoden wie folgt aus:

public abstract class MiddleTier : MarshalByRefObject 
{ 
    // Operation.Save is just an enum 

    [Task("Invoice", Operation.Save)] 
    public Invoice_Save(object pk, DataSet delta); 

    [Task("Receipt", Operation.Save)] 
    public Receipt_Save(object pk, DataSet delta); 


    // compiler cannot flag if someone deviates from team's standard 
    [Task("Receipt", Operation.Save)] 
    public Receipt_Save(object pk, object[] delta); 
} 

dann auf Laufzeit, ich werde alle die Methoden des Middlelaufen und sie zu Sammlung setzen (Attribute hilft viel hier), dann ordnet sie auf winform der Stellvertretung Funktionen (durch die Schnittstelle erleichtert, plugins-based system) als geladen

Ich denke, wenn ich die Attribute selbstbeschreibender machen kann, so kann der Compiler Inkonsistenzen feststellen.

namespace Craft 
{   
    // public delegate DataSet SaveDelegate(object pk, DataSet delta); // defined in TaskAttribute 

    public abstract class MiddleTier : MarshalByRefObject 
    { 

     [Task("Invoice", SaveDelegate)]   
     public abstract Invoice_Save(object pk, DataSet delta); 

     [Task("Receipt", SaveDelegate)] 
     // it's nice if the compiler can flag an error 
     public abstract Receipt_Save(object pk, object[] delta); 
    } 
} 

Ich denke ich, wenn die Methoden auf jeder Klasse setzen, wäre es zuviel des Guten sein, immer ein Objekt zu instanziiert Remoting. Und wenn man sie in eine separate Klasse einordnet, könnte es schwieriger sein, die Wiederverwendung von Code zu erleichtern, sagen wir, Invoice_Save benötigt einige Informationen über Receipt_Open. Tatsächlich habe ich sogar einen Bericht hier (crystal), der Daten von Remoting middeletier DataSet innerhalb der aufgerufenen Methode abruft, er bekommt einige Informationen über andere Methoden und fusioniert in seinem eigenen DataSet, sie geschehen alle auf Middlese, keine mehreren Roundtrips, Alle sind auf der Server-Seite (Mittelschicht)

+0

Welche Sprache ist das? – hlovdal

+0

Sprache ist C# – Hao

+0

Ich lege dies nicht auf eine Antwort, weil ich es überhaupt nicht geübt habe, aber vielleicht durch Reflexion könnte man (1) alle Methoden in der Klasse (2) für jede Methode überprüfen, ob es das hat gegebene Unterschrift und Attribut. –

Antwort

3

Sie könnten sowohl die FollowAttribute Sie in Ihrem Beispiel und write a Static Analysis (say, FxCop) rule, die überprüfen könnte, wenn eine Methode, die mit diesem Attribut markiert hat die gleiche Signatur wie die genannten Delegaten. So sollte es möglich sein.

1

Dies ist keine Sprache-Funktion, aber ...

Dies ist etwas, das Sie Validierung tun könnten: Schreib Unit-Tests, die über die Klasse zu reflektieren und fehlschlagen, wenn die Signatur nicht das Attribut Erklärung überein.

PostSharp gibt Ihnen auch einige interessante Möglichkeiten, dies rund um die Kompilierung zu tun. Ich weiß nicht, wie genau Sie es verwenden würden, aber ich vermute, Sie könnten ...

11

Andere Antworten sind natürlich gültig, aber nichts wird Sie davor schützen, zu vergessen, [Follow(AutoCompleteDelegate)] Attribut auf Ihre Methode anzuwenden.

Ich glaube, Sie würden besser dran machen Methoden in Klassen drehen, die eine Schnittstelle implementieren:

public interface IAutoComplete 
{ 
    DataSet Complete(string filter, long rowOffset); 
} 

public class CustomerAutoComplele : IAutoComplete 
{ 
    public DataSet Complete(string filter, long rowOffset) 
    { 
     var c = Connect(); 
     // some code here 
    } 
} 

und dann die factory method pattern verwenden, um Ihre „auto Beender“ zu erhalten:

public static class AutoCompleteFactory 
{ 
    public static IAutoComplete CreateFor(string purpose) 
    { 
     // build up and return an IAutoComplete implementation based on purpose. 
    } 
} 

oder

Sobald Sie das haben, können Sie einen Blick auf Inver werfen sion der Steuerung und der Abhängigkeitsinjektion, um eine harte Codierung der Liste der Autovervollständigungsimplementierungen in Ihrer Factory-Methode zu vermeiden.

+0

Es könnte einige Feinabstimmungen erfordern, da das Beispiel zwei übereinstimmende Methoden hat ... aber ich stimme generell zu. Heck sogar Generika würde (IAutoComplete etc) mit expliziter Schnittstellenimplementierung tun. –

+3

Ugh. Die Verwendung von Factory-Methoden, Inversion der Kontrolle und Dependency Injection sind Anzeichen dafür, dass Sie sich auf dem falschen Weg befinden. Jeder mag unschuldig genug erscheinen, aber fügen Sie sie alle zusammen und Sie bekommen ein übermäßig kompliziertes Durcheinander. Sie sollten damit aufhören, Methoden in Klassen umzuwandeln, die eine Schnittstelle implementieren. Nun, das ist ein Plan, der leicht zu verstehen ist. –

+0

Ja, ich stimme zu, dass es ein wenig übergeordnet sein könnte (der vollständige Ansatz), nur für eine einfache Autovervollständigung, aber das Schöne ist, dass alles inkrementell ist - Sie können wirklich an jedem Punkt anhalten und haben immer noch eine anständige Lösung. Ich hätte das deutlicher machen können. –

0

Attribute werden als zusätzliche Meta-Informationen, die während der Kompilierung gespeichert - man kann sie zur Laufzeit aber während der Kompilierung abfragen können sie nicht berücksichtigt werden

Sie können eine Methode durch ein Attribut nicht auf sie beschränken.. Sie können einschränken, wie das Attribut angewendet wird (d. H. Nur für Methoden oder ob mehrere angewendet werden können).

Ich würde mit FxCop vorschlagen zu warnen, wenn die Attribute nicht übereinstimmen - wenn Sie die Art und Weise Ereignisse vorsichtig sein, nicht Typ unterstützen wirft:

[Follow(AutoCompleteDelegate)] 
public DataSet Customer_AutoComplete(string filter, int rowOffset) 

Würde ein gültiger Delegierter sein.

0

Nr.

Sortieren von.

Sie können dieses Verhalten nicht zur Kompilierzeit erhalten. Sie können dies mit Attributen in eine einfache Testumgebung verschieben oder einen sofortigen Fehler erzwingen, wenn die enthaltene Klasse instanziiert wird.

Betrachten wir die beiden (schnell gehackt) Attribute:

[AttributeUsage(AttributeTargets.Class)] 
public class EnforceConforms : Attribute 
{ 
    public EnforceConforms(Type myClass) 
     : base() 
    { 
     MethodInfo[] info = myClass.GetMethods(); 

     foreach (MethodInfo method in info) 
     { 
      object[] objs = method.GetCustomAttributes(false); 

      foreach (object o in objs) 
      { 
       Attribute t = (Attribute)o; 

       if (t.GetType() != typeof(ConformsAttribute)) continue; 

       MethodInfo mustConformTo = ((ConformsAttribute)t).ConformTo; 

       ParameterInfo[] info1 = mustConformTo.GetParameters(); 
       ParameterInfo[] info2 = method.GetParameters(); 

       bool doesNotCoform = false; 

       doesNotCoform |= (mustConformTo.ReturnType != method.ReturnType); 
       doesNotCoform |= (info1.Length != info2.Length); 

       if (!doesNotCoform) 
       { 
        for (int i = 0; i < info1.Length; i++) 
        { 
         ParameterInfo p1 = info1[i]; 
         ParameterInfo p2 = info2[i]; 

         if (!p1.ParameterType.Equals(p2.ParameterType)) 
         { 
          doesNotCoform = true; 
          break; 
         } 
        } 
       } 

       if (doesNotCoform) 
       { 
        throw new Exception(myClass.Name + "." + method.Name + " does not conform to required delegate signature"); 
       } 
      } 
     } 
    } 
} 

[AttributeUsage(AttributeTargets.Method)] 
public class ConformsAttribute : Attribute 
{ 
    public MethodInfo ConformTo; 

    public ConformsAttribute(Type type) 
     : base() 
    { 
     if (type.BaseType != typeof(Delegate) && type.BaseType != typeof(System.MulticastDelegate)) throw new Exception("Can only accept delegates"); 

     ConformTo = type.GetMethod("Invoke"); 
    } 
} 

Wurf EnforceConforms (typeof (myFavoriteClass)) auf eine Klasse, und entspricht (typeof (myFavoriteDelegate)) auf den einschlägigen Methoden und dann (Diese ist der Hacky-Teil) typeof (myFavoriteClass) .GetCustomAttributes (false). Sie könnten dies in einem statischen Initialisierer tun, um "wirklich schnell" zu stürzen, oder in einer Testklasse (die nach allen Methoden in der Assembly mit dem EnforceConforms-Attribut sucht, wenn Sie etwas Besonderes wünschen).

Im Allgemeinen sollten Sie dies wahrscheinlich nicht verwenden. Wenn Ihr Entwurf erfordert, dass Sie die richtigen Delegiertenimplementierungen überprüfen, sollten Sie, wenn möglich, neu erstellen. Außerdem machen die nicht kompilierten Zeit-Bits es so, dass Sie sich nicht wirklich viel Zeit sparen.

1

Ich würde fragen, warum Sie das tun möchten. Wenn Sie nicht möchten, dass die Klasse durch Vererbung geändert wird, können Sie sie zu einer versiegelten Klasse machen. Wenn Sie befürchten, dass jemand die Klasse in der Zukunft wechseln könnte, haben Sie einen von zwei Fällen. 1) Sie verstehen nicht, was sie tun; Nichts kann einen schlechten Programmierer daran hindern, schlimme Dinge zu tun, wenn er die volle Autorität hat, Programmtext zu bearbeiten. 2) Sie erweitern die Klassenfunktionalität auf eine Weise, die Sie derzeit nicht verstehen, was die Wiederverwendung beeinträchtigt.

Verwandte Themen