web-dev-qa-db-fra.com

Remplacer le constructeur par défaut d'une classe partielle par une autre classe partielle

Je ne pense pas que ce soit possible, mais si c'est le cas, j'en ai besoin :)

J'ai un fichier proxy généré automatiquement à partir de l'outil de ligne de commande wsdl.exe de Visual Studio 2008.

La sortie proxy est des classes partielles. Je veux remplacer le constructeur par défaut qui est généré. Je préférerais ne pas modifier le code car il est généré automatiquement.

J'ai essayé de créer une autre classe partielle et de redéfinir le constructeur par défaut, mais cela ne fonctionne pas. J'ai ensuite essayé d'utiliser le remplacement et les nouveaux mots clés, mais cela ne fonctionne pas.

Je sais que je pourrais hériter de la classe partielle, mais cela voudrait dire que je devrais changer tout notre code source pour qu'il pointe vers la nouvelle classe parente. Je préférerais ne pas avoir à faire cela.

Des idées, des solutions de rechange ou des 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...
      }
   }
}
51
Elijah Manor

Ce n'est pas possible . Les classes partielles sont essentiellement des parties de la même classe; aucune méthode ne peut être définie deux fois ou remplacée, ce qui inclut le constructeur.

Vous pouvez appeler une méthode dans le constructeur et l'implémenter uniquement dans l'autre fichier de pièce.

37
configurator

J'ai eu un problème similaire, mon code généré étant créé par un fichier dbml (j'utilise les classes Linq-to-SQL).

Dans la classe générée, il appelle un vide partiel appelé OnCreated () à la fin du constructeur.

En résumé, si vous souhaitez conserver les informations importantes relatives au constructeur que la classe générée fait pour vous (ce que vous devriez probablement faire), créez dans votre classe partielle les éléments suivants:

partial void OnCreated()
{
    // Do the extra stuff here;
}
66
Tom Chantler

Hmmm, Je pense qu'une solution élégante serait la suivante:

//* 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
        }
    }
}

Cette approche a quelques inconvénients (comme tout):

  1. Il n'est pas clair quand exactement le constructeur de la classe interne MyCustomization sera exécuté pendant toute la procédure de construction de la classe AutogenCls.

  2. S'il est nécessaire d'implémenter l'interface IDiposable pour que la classe MyCustomization gère correctement la suppression des ressources non gérées de la classe MyCustomization, je ne sais pas (encore) comment déclencher la méthode MyCustomization.Dispose () sans toucher au fichier AutogenCls.cs. ... (mais comme je l'ai dit 'encore' :)

Mais cette approche offre une grande séparation du code généré automatiquement - la personnalisation complète est séparée dans un fichier de code src différent.

prendre plaisir :)

13
user307938

Le problème rencontré par l'OP est que le proxy de référence Web ne génère aucune méthode partielle que vous pouvez utiliser pour intercepter le constructeur.

J'ai rencontré le même problème et je ne peux pas me contenter de passer à WCF car le service Web que je cible ne le prend pas en charge.

Je ne voulais pas modifier manuellement le code généré automatiquement, car il serait aplati si quelqu'un invoquait la génération de code.

J'ai abordé le problème sous un angle différent. Je savais que mon initialisation devait être faite avant une requête, ce n'était pas vraiment le cas au moment de la construction, alors j'ai simplement surchargé la méthode GetWebRequest de la sorte.

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;
}

C'est une solution intéressante, car elle n'implique pas de piratage du code généré automatiquement et correspond au cas d'utilisation exact de l'OP consistant à effectuer la connexion à l'initialisation d'un proxy généré automatiquement par SoapHttpClientProtocol.

3
Doctor Jones

En fait, cela est maintenant possible, maintenant que des méthodes partielles ont été ajoutées. Voici le doc: 

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

En gros, l’idée est que vous pouvez déclarer et appeler une méthode dans un fichier dans lequel vous définissez la classe partielle, sans pour autant définir la méthode dans ce fichier. Dans l'autre fichier, vous pouvez ensuite définir la méthode. Si vous construisez un assembly où la méthode n'est pas définie, l'ORM supprimera tous les appels à la fonction.

Donc, dans le cas ci-dessus, cela ressemblerait à ceci:

// classe générée automatiquement

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

partial void OtherCode();

// classe créée manuellement afin de remplacer le constructeur par défaut

partial void OtherCode()
{
   //do whatever extra stuff you wanted.
}

C'est un peu limité, et dans ce cas particulier, où vous avez un fichier généré que vous devez modifier, ce n'est peut-être pas la bonne solution, mais pour d'autres qui sont tombés sur cette tentative d'essayer de surcharger la fonctionnalité dans des classes partielles, cela peut être très utile.

3
rrreee

Tu ne peux pas faire ça. Je suggère d'utiliser une méthode partielle pour laquelle vous pouvez ensuite créer une définition. Quelque chose comme:

public partial class MyClass{ 

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

    public partial void OnCreated();
}

Le reste devrait être assez explicite.

MODIFIER: 

J'aimerais également souligner que vous devez définir une interface pour ce service, sur laquelle vous pouvez ensuite programmer, afin de ne pas avoir à faire référence à l'implémentation réelle. Si vous faisiez cela, vous auriez quelques autres options.

2
jonnii

Je pense que vous pourriez le faire avec PostSharp , et il semblerait que quelqu'un a fait exactement ce que vous voulez pour les méthodes des classes partielles générées . Je ne sais pas si cela se traduira facilement par la possibilité d'écrire une méthode et de faire en sorte que son corps remplace le constructeur, car je ne l'ai pas encore essayé, mais cela semble valoir le coup.

Edit: ceci va dans le même sens et semble également intéressant.

2
cfeduke

Ceci est à mon avis un défaut de conception dans la langue. Ils auraient dû autoriser plusieurs implémentations d'une méthode partielle, ce qui aurait fourni une solution de Nice . De manière encore plus agréable, le constructeur (également une méthode) peut alors aussi être simplement marqué comme partiel et plusieurs constructeurs avec la même signature exécuté lors de la création d'un objet.

La solution la plus simple consiste probablement à ajouter une méthode partielle de 'constructeur' par classe partielle partielle:

public partial class MyClass{ 

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

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

Si vous voulez que les classes partielles soient agnostiques les unes par rapport aux autres, vous pouvez utiliser la réflexion:

// 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));
    }

}

Mais en réalité, ils devraient simplement ajouter quelques constructions de langage supplémentaires pour travailler avec des classes partielles.

1
nreyntje

Rien que je puisse penser. Le "meilleur" moyen que je puisse trouver est d'ajouter un ctor avec un paramètre factice et de l'utiliser

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);
0
James Curran

Pour un proxy de service Web généré par Visual Studio, vous ne pouvez pas ajouter votre propre constructeur dans la classe partielle (vous pouvez le faire, mais il n'est pas appelé). À la place, vous pouvez utiliser l'attribut [OnDeserialized] (ou [OnDeserializing]) pour insérer votre propre code au point où la classe de proxy Web est instanciée.

using System.Runtime.Serialization;

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

Parfois, vous n'avez pas d'accès ou il n'est pas permis de changer le constructeur par défaut. Pour cette raison, vous ne pouvez pas avoir le constructeur par défaut pour appeler des méthodes.

Dans ce cas, vous pouvez créer un autre constructeur avec un paramètre factice et faire en sorte que ce nouveau constructeur appelle le constructeur par défaut à l'aide de ": this ()"

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

Et lorsque vous créez une nouvelle instance de cette classe, il vous suffit de passer un paramètre factice comme ceci:

SomeClass objSomeClass = new SomeClass(0);
0
Shadi