Переопределить конструктор по умолчанию частичного класса другим частичным классом

Я не думаю, что это возможно, но если это так, то мне это нужно:)

У меня есть автоматически сгенерированный прокси-файл из 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...
      }
   }
}

11 ответов


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

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


У меня был аналогичный prolem, с моим сгенерированным кодом, созданным файлом dbml (я usng Linq-to-SQL classes).

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

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

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

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

//* 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 файл ... (но как я уже сказал "Пока":)

но этот подход предлагает отличное отделение от автоматически сгенерированного кода - вся настройка разделена в другом файле кода src.

наслаждайтесь :)


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

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

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


проблема, которую имеет 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 автоматически сгенерированного прокси.


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

public partial class MyClass{ 

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

    public partial void OnCreated();
}

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

изменить:

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


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

Edit:это примерно то же самое а также выглядит интересно.


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

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

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

}

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


ничего, что я могу думать. "Лучший" способ, который я могу придумать, - добавить 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);

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

using System.Runtime.Serialization;

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

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

в этом случае вы можете создать другой конструктор с фиктивным параметром и сделать этот новый конструктор для вызова конструктора по умолчанию, используя ": this ()"

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

и когда вы создаете новый экземпляр этого класса, вы просто передаете фиктивный параметр следующим образом:

SomeClass objSomeClass = new SomeClass(0);