2008-10-29 3 views
47

Я не думаю, что это возможно, но если есть, то мне это нужно :)Override По умолчанию Конструктор частичного класса с другой частичным классом

У меня есть автоматически сгенерированный прокси-файл из командной строки wsdl.exe инструмента Visual Studio 2008.

Выход прокси-сервера является частичным классом. Я хочу переопределить созданный по умолчанию конструктор. Я бы предпочел не изменять код, поскольку он автогенерируется.

Я попытался сделать еще один частичный класс и переопределить конструктор по умолчанию, но это не сработает. Затем я попытался использовать переопределение и новые ключевые слова, но это не работает.

Я знаю, что я мог бы наследовать от частичного класса, но это означало бы, что мне придется изменить весь наш исходный код, чтобы указать на новый родительский класс. Я бы предпочел не делать этого.

Любые идеи, работа вокруг или хаки?

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

ответ

36

Это не представляется возможным. Частичные классы по существу являются частью одного и того же класса; ни один метод не может быть определен дважды или переопределен, и он включает конструктор.

Вы можете вызвать метод в конструкторе и реализовать его только в другом файле детали.

0

Ничего, что я могу придумать. «Лучший» способ, которым я могу придумать, чтобы добавить CTOR с фиктивным параметром и использовать это:

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

Вы не можете этого сделать. Я предлагаю использовать частичный метод, который затем можно создать для определения. Что-то вроде:

public partial class MyClass{ 

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

    public partial void OnCreated(); 
} 

Остальное должно быть довольно понятно.

EDIT:

Я хотел бы также отметить, что вы должны быть определения интерфейса для этого сервиса, который вы можете затем программа, так что вы не должны иметь ссылки на фактической реализации. Если вы это сделаете, у вас будет еще несколько вариантов.

2

Я думаю, вы можете сделать это с помощью PostSharp, и похоже, что кто-то сделал именно то, что вы want for methods in generated partial classes. Я не знаю, легко ли это перевести на способность писать метод и заменить его конструктором, поскольку я еще не сделал его выстрелом, но кажется, что это стоит того.

Редактировать: this is along the same lines а также выглядит интересным.

66

У меня был аналогичный пролет, с моим сгенерированным кодом, созданным файлом dbml (я использую классы Linq-to-SQL).

В сгенерированном классе он вызывает частичную пустоту с именем OnCreated() в конце конструктора.

Короче говоря, если вы хотите сохранить важный конструктор материал сгенерированный класс делает для вас (который вы, вероятно, должны делать), то в вашем частичном классе создать следующее:

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

+1 Простое и элегантное решение. – James 2011-06-13 23:25:30

+3

Теперь это дилемма для голосования ... на самом деле не имеет ничего общего с вопросом OP, который не связан с L2S, поэтому у него не будет OnCreated, но вы остановили меня, постукивая головой о стол, так что я думаю. – Ryan 2011-07-15 18:44:42

+0

@ Ryan: Рад помочь. Спасибо :-) – 2011-08-23 15:42:23

12

Хммм, Я думаю, что один элегантное решение будет следующим:

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

Этот подход имеет некоторые недостатки (как и все):

  1. Непонятно, когда именно будет выполняться конструктор внутреннего класса MyCustomization во время всей процедуры построения класса AutogenCls.

  2. Если вам необходимо реализовать интерфейс IDiposable для класса MyCustomization, чтобы правильно обрабатывать удаление неуправляемых ресурсов класса MyCustomization, я пока не знаю, как вызвать метод MyCustomization.Dispose(), не касаясь файл AutogenCls.cs ... (но, как я сказал «пока» :)

но этот подход предлагает большой отрыв от автоматического сгенерированных кода - вся настройка отделена в различной ЦСИ файле коды.

наслаждаться :)

1

Это на мой взгляд, недостаток дизайна в языке. Они должны были допускать несколько реализаций одного частичного метода, что обеспечило бы хорошее решение. Еще лучше, конструктор (также метод) также может быть просто отмечен частичным, а при создании объекта будут выполняться несколько конструкторов с одной и той же сигнатурой.

Самое простое решение, вероятно, добавить еще один частичный метод «конструктор» за дополнительный частичный класс:

public partial class MyClass{ 

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

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

Если вы хотите частные классы, чтобы быть агностиком друг о друге, вы можете использовать отражение:

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

} 

Но на самом деле им нужно просто добавить несколько языковых конструкций для работы с частичными классами.

0

Для прокси-сервера веб-службы, сгенерированного Visual Studio, вы не можете добавить свой собственный конструктор в частичный класс (ну, вы можете, но он не вызван). Вместо этого вы можете использовать атрибут [OnDeserialized] (или [OnDeserializing]) для подключения своего собственного кода в точке, где создается веб-прокси-класс.

using System.Runtime.Serialization; 

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

Фактически, теперь это возможно, теперь, когда добавлены частичные методы. Вот документ:

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

В принципе, идея заключается в том, что вы можете объявлять и вызывать метод в одном файле, где вы определяете частичный класс, но на самом деле не определить метод в этом файле. В другом файле вы можете определить метод. Если вы строите сборку, где метод не определен, ORM удалит все вызовы функции.

Таким образом, в вышеописанном случае она будет выглядеть следующим образом:

// Авто-порожденный класс

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

partial void OtherCode(); 

// Вручную созданный класс для того, чтобы переопределить конструктор по умолчанию

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

Он несколько ограничен, и в этом конкретном случае, когда у вас есть сгенерированный файл, который вам нужно изменить, это может быть не самое правильное решение, но для других, которые наткнулись на эту попытку чтобы переопределить функциональность в частичных классах, это может быть весьма полезно.

0

Иногда у вас нет доступа или не разрешено изменять конструктор по умолчанию, поэтому вы не можете использовать конструктор по умолчанию для вызова каких-либо методов.

В этом случае вы можете создать еще один конструктор с фиктивным параметром, и сделать этот новый конструктор вызвать конструктор по умолчанию с помощью «: это()»

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

И когда вы создаете новый экземпляр этого класс просто передать фиктивный параметр, как это:

SomeClass objSomeClass = new SomeClass(0); 
2

Проблема, что OP получил то, что веб-ссылку прокси не создает каких-либо частичных методов, которые можно использовать для перехвата конструктора.

Я столкнулся с той же проблемой, и я не могу просто перейти на WCF, потому что веб-сервис, который я нацеливаю, не поддерживает его.

Я не хотел вручную изменять код с автогенерированием, потому что он будет сглажен, если кто-либо когда-нибудь вызовет генерацию кода.

Я решил проблему под другим углом. Я знал, что моя инициализация должна выполняться перед запросом, на самом деле это не нужно было делать во время строительства, поэтому я просто переопределял метод 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; 
} 

Это хорошее решение, потому что оно не связанно с хакерством автогенерируемого кода, и он подходит точно случаю использования в OP еще выполнение инициализации входа для SoapHttpClientProtocol автогенерируемой прокси.

Смежные вопросы