2008-10-29 9 views
47

Ich glaube nicht, das ist möglich, aber wenn dann ich es brauche :)außer Kraft setzt Standard-Konstruktor von Teil Klasse mit anderem Teil Klasse

Ich habe eine automatisch generierte Proxy-Datei aus dem wsdl.exe Befehlszeile Werkzeug von Visual Studio 2008.

Die Proxy-Ausgabe ist partielle Klassen. Ich möchte den Standardkonstruktor, der generiert wird, überschreiben. Ich würde den Code lieber nicht ändern, da er automatisch generiert wird.

Ich habe versucht, eine andere Teilklasse und die Neudefinition des Standardkonstruktors, aber das funktioniert nicht. Ich habe dann versucht, die Überschreibung und neue Schlüsselwörter zu verwenden, aber das funktioniert nicht.

Ich weiß, ich könnte von der partiellen Klasse erben, aber das würde bedeuten, dass ich unseren gesamten Quellcode ändern müsste, um auf die neue Elternklasse zu zeigen. Ich hätte das lieber nicht.

Irgendwelche Ideen, Arbeitsumgebungen oder Hacks?

//Auto-generated class 
namespace MyNamespace { 
    public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol { 
     public MyWebService() { 
     string myString = "auto-generated constructor"; 
     //other code... 
     } 
    } 
} 

//Manually created class in order to override the default constructor 
namespace MyNamespace { 
    public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol { 
     public override MyWebService() { //this doesn't work 
     string myString = "overridden constructor"; 
     //other code... 
     } 
    } 
} 

Antwort

36

Das ist nicht möglich. Teilklassen sind im Wesentlichen Teile derselben Klasse; Keine Methode kann zweimal definiert oder außer Kraft gesetzt werden, und das schließt den Konstruktor ein.

Sie könnten eine Methode im Konstruktor aufrufen und sie nur in der anderen Teildatei implementieren.

0

Nichts, was mir einfällt. Die „besten“ Weg ich mit oben kommen kann ist ein Ctor mit einem Dummy-Parameter hinzufügen und verwenden, die:

public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol 
{ 
    public override MyWebService(int dummy) 
    { 
     string myString = "overridden constructor"; 
     //other code... 
    } 
} 


MyWebService mws = new MyWebService(0); 
2

Sie können dies nicht tun. Ich schlage vor, eine partielle Methode zu verwenden, für die Sie dann eine Definition erstellen können. Etwas wie:

public partial class MyClass{ 

    public MyClass(){ 
     ... normal construction goes here ... 
     AfterCreated(); 
    } 

    public partial void OnCreated(); 
} 

Der Rest sollte ziemlich selbsterklärend sein.

EDIT:

ich auch, dass Sie eine Schnittstelle für diesen Dienst sollte darauf hinweisen, möchte sein zu definieren, die Sie dann Programm, so dass Sie sich keine Hinweise auf die tatsächliche Umsetzung haben. Wenn Sie das getan hätten, hätten Sie ein paar andere Möglichkeiten.

2

Ich denke, Sie könnten dies mit PostSharp tun, und es sieht aus wie jemand getan hat, was Sie gerade want for methods in generated partial classes. Ich weiß nicht, ob dies leicht zu der Fähigkeit führen wird, eine Methode zu schreiben und seinen Körper den Konstruktor ersetzen zu lassen, da ich ihm noch keinen Schuss gegeben habe, aber es scheint einen Versuch wert zu sein.

Edit: this is along the same lines und sieht auch interessant aus.

66

Ich hatte eine ähnliche prelem, wobei mein generierter Code von einer dbml-Datei erstellt wurde (ich benutze Linq-to-SQL-Klassen).

In der generierten Klasse ruft es am Ende des Konstruktors eine partielle Lücke namens OnCreated() auf.

Lange Rede kurzer Sinn, wenn Sie die wichtigen Konstruktor Sachen die generierte Klasse tut, für Sie (die Sie wahrscheinlich tun sollte) behalten wollen, dann in Ihrem Teil Klasse erstellen Sie die folgende:

partial void OnCreated() 
{ 
    // Do the extra stuff here; 
} 
+1

+1 Einfache und elegante Lösung. – James

+3

Jetzt ist dies ein Wahldilemma ... nicht wirklich etwas mit OP-Frage zu tun, die nicht über L2S ist, also wird kein OnCreated haben, aber Sie haben mich aufgehört, meinen Kopf gegen den Tisch so +1 schlagen, denke ich. – Ryan

+0

@Ryan: Ich bin froh, dass ich geholfen habe. Danke :-) –

12

Hmmm, ich denke, eine elegante Lösung die folgende wäre:

//* AutogenCls.cs file 
//* Let say the file is auto-generated ==> it will be overridden each time when 
//* auto-generation will be triggered. 
//* 
//* Auto-generated class, let say via xsd.exe 
//* 
partial class AutogenCls 
{ 
    public AutogenCls(...) 
    { 
    } 
} 



//* AutogenCls_Cunstomization.cs file 
//* The file keeps customization code completely separated from 
//* auto-generated AutogenCls.cs file. 
//* 
partial class AutogenCls 
{ 
    //* The following line ensures execution at the construction time 
    MyCustomization m_MyCustomizationInstance = new MyCustomization(); 

    //* The following inner&private implementation class implements customization. 
    class MyCustomization 
    { 
     MyCustomization() 
     { 
      //* IMPLEMENT HERE WHATEVER YOU WANT TO EXECUTE DURING CONSTRUCTION TIME 
     } 
    } 
} 

Dieser Ansatz hat einige Nachteile (wie alles):

  1. Es ist nicht klar, wann genau der Konstruktor der internen MyCustomization-Klasse während des gesamten Konstruktionsverfahrens der AutogenCls-Klasse ausgeführt wird.

  2. Wenn die IDiposable-Schnittstelle für die MyCustomization-Klasse implementiert werden muss, damit die nicht verwalteten Ressourcen der MyCustomization-Klasse ordnungsgemäß entsorgt werden, weiß ich (noch) nicht, wie die MyCustomization.Dispose() -Methode ohne Berührung ausgelöst wird die AutogenCls.cs Datei ... (aber wie ich sagte ‚noch‘ :)

aber dieser Ansatz bietet große Trennung von automatisch generierten Code - ganzer Anpassung in verschiedener src-Code-Datei getrennt ist.

genießen :)

+0

StyleCop wird sich über diese Lösung beschweren: Sie sollten nicht verwendete private Variablen vermeiden. –

+1

Diese Lösung bietet nur einen statischen Konstruktor: keinen Zugriff auf 'this'. –

+0

Eine kleine Vereinfachung, anstatt einer 'Klasse MyCustomization' müssen Sie nur' Task _customization = TaskEx.Run (async() => {/ * Do Anpassung * /}); '. Und Async kann weggelassen werden, wenn Sie es nicht brauchen. –

1

Dies ist meiner Meinung nach ein Konstruktionsfehler in der Sprache. Sie hätten mehrere Implementierungen einer Teilmethode erlauben sollen, die eine schöne Lösung geliefert hätte. In noch netterer Weise kann der Konstruktor (auch eine Methode) dann auch einfach als partiell markiert werden und mehrere Konstruktoren mit der gleichen Signatur würden beim Erstellen eines Objekts laufen.

Die einfachste Lösung ist wahrscheinlich eine Teil ‚Konstruktor‘ Methode für jede zusätzliche Teil Klasse hinzuzufügen:

public partial class MyClass{ 

    public MyClass(){ 
     ... normal construction goes here ... 
     OnCreated1(); 
     OnCreated2(); 
     ... 
    } 

    public partial void OnCreated1(); 
    public partial void OnCreated2(); 
} 

Wenn Sie die Teilklassen umeinander Agnostiker sein wollen, können Sie Reflexion verwenden:

// In MyClassMyAspect1.cs 
public partial class MyClass{ 

    public void MyClass_MyAspect2(){ 
     ... normal construction goes here ... 

    } 

} 

// In MyClassMyAspect2.cs 
public partial class MyClass{ 

    public void MyClass_MyAspect1(){ 
     ... normal construction goes here ... 
    } 
} 

// In MyClassConstructor.cs 
public partial class MyClass : IDisposable { 

    public MyClass(){ 
     GetType().GetMethods().Where(x => x.Name.StartsWith("MyClass")) 
          .ForEach(x => x.Invoke(null)); 
    } 

    public void Dispose() { 
     GetType().GetMethods().Where(x => x.Name.StartsWith("DisposeMyClass")) 
          .ForEach(x => x.Invoke(null)); 
    } 

} 

Aber wirklich sollten sie nur einige Sprachkonstrukte hinzufügen, um mit Teilklassen zu arbeiten.

0

Für einen Web-Service-Proxy, der von Visual Studio generiert wird, können Sie keinen eigenen Konstruktor in der partiellen Klasse hinzufügen (Sie können das, aber es wird nicht aufgerufen). Stattdessen können Sie das Attribut [OnDeserialized] (oder [OnDeserializing]) verwenden, um Ihren eigenen Code an dem Punkt einzubinden, an dem die Web-Proxy-Klasse instanziiert wird.

using System.Runtime.Serialization; 

partial class MyWebService 
{ 
    [OnDeserialized] 
    public void OnDeserialized(StreamingContext context) 
    { 
     // your code here 
    } 
} 
+0

Ist dies für den Webdienst oder für Objekte, die deserialisiert werden, da sie in einem Serviceaufruf zurückgegeben werden? Ich habe versucht, es meiner partiellen Klasse meines Web-Service-Clients hinzuzufügen, aber meine Methode wird nicht aufgerufen ... –

4

Eigentlich ist dies jetzt möglich, jetzt, da Teilmethoden hinzugefügt wurden. Hier ist das doc:

http://msdn.microsoft.com/en-us/library/wa80x488.aspx

Grundsätzlich ist die Idee ist, dass Sie eine Methode in einer Datei deklarieren und aufrufen können, wo Sie die Teil-Klasse definieren, aber nicht wirklich die Methode in dieser Datei definieren. In der anderen Datei können Sie dann die Methode definieren. Wenn Sie eine Assembly erstellen, für die die Methode nicht definiert ist, entfernt das ORM alle Aufrufe an die Funktion.

So im Fall oben, es würde wie folgt aussehen:

// Auto-generierte Klasse

namespace MyNamespace { 
    public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol { 
     public MyWebService() { 
     string myString = "auto-generated constructor"; 
     OtherCode(); 
     } 
    } 
} 

partial void OtherCode(); 

// Manuell erstellte Klasse, um den Standard-Konstruktor

partial void OtherCode() 
{ 
    //do whatever extra stuff you wanted. 
} 
außer Kraft zu setzen

Es ist etwas begrenzt, und in diesem speziellen Fall, wo Sie eine generierte Datei haben, die Sie ändern müssen, ist es möglicherweise nicht die richtige Lösung, aber für andere, die bei diesem Versuch stolperten Um die Funktionalität in Teilklassen zu überschreiben, kann dies sehr hilfreich sein.

+5

Das große Problem ist, dass der automatisch generierte Code dies implementieren muss, aber in vielen Fällen habe ich keine Kontrolle über den Autogen-Code – VoteCoffee

0

Manchmal haben Sie keinen Zugriff oder es ist nicht erlaubt, den Standardkonstruktor zu ändern. Aus diesem Grund kann der Standardkonstruktor keine Methoden aufrufen.

In diesem Fall, dass Sie einen anderen Konstruktor mit einem Dummy-Parameter erstellen, und diese neue Konstruktor machen den Standard-Konstruktor aufzurufen mit „: Mit dieser()“

public SomeClass(int x) : this() 
{ 
    //Your extra initialization here 
} 

Und wenn Sie eine neue Instanz dieses erstellen Klasse, die Sie passieren nur Dummy-Parameter wie folgt aus:

SomeClass objSomeClass = new SomeClass(0); 
2

das Problem, das die OP bekommen hat, ist, dass die Web-Referenz-Proxy alle Teil Methoden nicht generieren, die Sie den Konstruktor abfangen können.

Ich stieß auf das gleiche Problem, und ich kann nicht einfach auf WCF aktualisieren, weil der Webdienst, den ich anvisiere, es nicht unterstützt.

Ich wollte den automatisch generierten Code nicht manuell ändern, da er abgeflacht wird, wenn jemand die Codegenerierung aufruft.

Ich ging das Problem aus einem anderen Blickwinkel an. Ich wusste, dass meine Initialisierung vor einer Anfrage ausgeführt werden musste, es musste nicht wirklich zur Konstruktionszeit getan werden, also überging ich die GetWebRequest-Methode einfach so.

protected override WebRequest GetWebRequest(Uri uri) 
{ 
    //only perform the initialization once 
    if (!hasBeenInitialized) 
    { 
     Initialize(); 
    } 

    return base.GetWebRequest(uri); 
} 

bool hasBeenInitialized = false; 

private void Initialize() 
{ 
    //do your initialization here... 

    hasBeenInitialized = true; 
} 

Dies ist eine schöne Lösung, weil es nicht verwickeln sich Hacker die Auto-Code erzeugt, und es passt den genauen Anwendungsfall des OP der Initialisierung Login für einen Soaphttpclientprotocol automatisch generierten Proxy durchführen.