2008-10-29 21 views
47

no creo que esto es posible, pero si es que entonces yo :) necesitaAnulación constructor por defecto de la clase parcial con otra clase parcial

Tengo un archivo de proxy generada automáticamente desde la línea de comandos wsdl.exe herramienta de Visual Studio 2008.

La salida del proxy es de clases parciales. Quiero sobrescribir el constructor predeterminado que se genera. Preferiría no modificar el código ya que se genera automáticamente.

Intenté hacer otra clase parcial y redefinir el constructor predeterminado, pero eso no funciona. Luego intenté usar la anulación y las nuevas palabras clave, pero eso no funciona.

Sé que podría heredar de la clase parcial, pero eso significaría que tendría que cambiar todo nuestro código fuente para apuntar a la nueva clase principal. Preferiría no tener que hacer esto.

¿Alguna idea, solución de problemas o pirateo?

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

Respuesta

36

Esto no es posible. Las clases parciales son esencialmente partes de la misma clase; ningún método puede definirse dos veces o anularse, y eso incluye el constructor.

Puede llamar a un método en el constructor y solo implementarlo en el otro archivo de parte.

0

Nada que se me ocurra. La "mejor" manera de que pueda llegar a es añadir un ctor con un parámetro ficticio y el uso que:

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

No puede hacer esto. Sugiero usar un método parcial para el que luego puedes crear una definición. Algo así como:

public partial class MyClass{ 

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

    public partial void OnCreated(); 
} 

El resto debe ser bastante auto explicativo.

EDIT:

También me gustaría señalar que debe ser la definición de una interfaz de este servicio, que luego se puede programa para, por lo que no tiene que tener referencias a la aplicación real. Si hicieras esto, tendrías algunas otras opciones.

66

Tuve un problema similar, con mi código generado creado por un archivo dbml (estoy usando clases de Linq-a-SQL).

En la clase generada llama a un vacío parcial llamado OnCreated() al final del constructor.

Para resumir, si desea mantener las cosas constructor importante la clase generada hace por usted (que probablemente debería hacer), entonces en su clase parcial crear lo siguiente:

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

+1 Solución simple y elegante. – James

+3

Ahora este es un dilema de votación ... no tiene nada que ver con la pregunta de OP que no es sobre L2S, entonces no tendrá un OnCreated pero me has detenido golpeando mi cabeza contra la mesa, así que +1 creo. – Ryan

+0

@Ryan: Me alegro de haber sido de ayuda. Gracias :-) –

12

Hmmm, Creo que una solución elegante sería la siguiente:

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

Este enfoque tiene algunos inconvenientes (como todo):

  1. No está claro exactamente cuándo se ejecutará el constructor de la clase interna MyCustomization durante todo el procedimiento de construcción de la clase AutogenCls.

  2. Si será necesario implementar una interfaz IDiposable para que la clase MyCustomization maneje correctamente la eliminación de recursos no administrados de la clase MyCustomization, no sé (todavía) cómo activar el método MyCustomization.Dispose() sin tocar el archivo AutogenCls.cs ... (pero como le dije 'todavía' :)

pero este enfoque ofrece una gran separación de código generado automáticamente - personalización entera se separa en diferentes src archivo de código.

disfrutar :)

+0

StyleCop se quejará de esta solución: debe evitar las variables privadas no utilizadas. –

+1

Esta solución solo ofrece un constructor estático: no tiene acceso a 'this'. –

+0

Una pequeña simplificación, en lugar de una 'clase MyCustomization', solo necesita declarar' Task _customization = TaskEx.Run (async() => {/ * Do customization * /}); '. Y 'async' se puede omitir si no lo necesita. –

1

Esto es en mi opinión un fallo de diseño en el idioma. Deberían haber permitido implementaciones múltiples de un método parcial, que habría proporcionado una buena solución. De una forma aún más agradable, el constructor (también un método) también se puede marcar simplemente de forma parcial y múltiples constructores con la misma firma se ejecutarían al crear un objeto.

La solución más sencilla es probablemente añadir un método parcial 'constructor' por clase parcial extra:

public partial class MyClass{ 

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

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

Si desea que las clases parciales para ser agnóstico el uno del otro, se puede utilizar la reflexión:

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

} 

Pero realmente solo deberían agregar algunas construcciones de lenguaje más para trabajar con clases parciales.

0

Para un proxy de servicio web generado por Visual Studio, no puede agregar su propio constructor en la clase parcial (así puede hacerlo, pero no se llama). En su lugar, puede usar el atributo [OnDeserialized] (o [OnDeserializing]) para enganchar su propio código en el punto donde se crea una instancia de la clase de proxy web.

using System.Runtime.Serialization; 

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

¿Es esto para el servicio web o para objetos que se deserializan cuando se devuelven en una llamada de servicio? Intenté agregarlo a mi clase parcial de mi cliente de servicio web pero mi método no se llama ... –

4

En realidad, esto ahora es posible, ahora que se han agregado métodos parciales. Aquí está el doc:

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

Básicamente, la idea es que se puede declarar y llamar a un método en un archivo en el que está definiendo la clase parcial, pero en realidad no definir el método en ese archivo. En el otro archivo, puede definir el método. Si está construyendo un ensamblaje donde el método no está definido, entonces el ORM eliminará todas las llamadas a la función.

Así que en el caso anterior se vería así:

// auto generada clase

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

partial void OtherCode(); 

// creado manualmente clase con el fin de anular el constructor por defecto

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

Es algo limitado, y en este caso particular, donde tiene un archivo generado que tendría que modificar, podría no ser la solución correcta, pero para otros que tropezaron con este intento para anular la funcionalidad en clases parciales, esto puede ser bastante útil.

+5

El gran problema es que el código generado automáticamente debe implementar esto, pero en muchos casos no tengo control sobre el código de autogen – VoteCoffee

0

A veces no tiene acceso o no está permitido cambiar el constructor predeterminado, por esta razón no puede tener el constructor predeterminado para llamar a ningún método.

En este caso se puede crear otro constructor con un parámetro ficticio, y hacer de este nuevo constructor para llamar al constructor por defecto usando ": este()"

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

Y cuando se crea una nueva instancia de este la clase que acaba de pasar parámetro ficticio como esto:

SomeClass objSomeClass = new SomeClass(0); 
2

el problema que el PO ha conseguido es que el proxy web de referencia no genera ningún métodos parciales que se pueden utilizar para interceptar el constructor.

Me encontré con el mismo problema, y ​​no puedo simplemente actualizar a WCF porque el servicio web al que estoy apuntando no lo admite.

No quise modificar manualmente el código autogenerado porque se aplastará si alguien invoca la generación del código.

Abordé el problema desde un ángulo diferente. Sabía que mi inicialización era necesaria antes de una solicitud, realmente no era necesario hacerlo en el momento de la construcción, así que simplemente eliminé el método GetWebRequest.

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

Ésta es una buena solución, ya que no se trata de hackear el código de auto generada, y se ajusta caso de uso exacto de la OP de llevar a cabo la inicialización de inicio de sesión para un proxy generado SoapHttpClientProtocol automático.

Cuestiones relacionadas