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

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

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

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

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

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

Есть идеи, обходные пути или хаки?

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

person Elijah Manor    schedule 29.10.2008    source источник


Ответы (12)


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

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

person configurator    schedule 29.10.2008

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

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

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

partial void OnCreated()
{
    // Do the extra stuff here;
}
person Tom Chantler    schedule 09.01.2010
comment
Теперь это дилемма голосования ... на самом деле не имеет ничего общего с вопросом OP, который не касается L2S, поэтому у меня не будет OnCreated, но вы остановили меня, биться головой о стол, так что +1, я думаю. - person Ryan; 15.07.2011
comment
@ Райан: Рад, что помог. Спасибо :-) - person Tom Chantler; 23.08.2011
comment
Есть ли эквивалент для клиентов WCF? Они объявлены как частичные классы, но, похоже, не имеют метода OnCreated, позволяющего мне что-либо делать. Что-то мне не хватает? Это очень раздражает. - person Doctor Jones; 07.03.2014

Хммм, я думаю, что одним из элегантных решений было бы следующее:

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

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

person Community    schedule 02.04.2010
comment
StyleCop будет жаловаться на это решение: вам следует избегать неиспользуемых частных переменных. - person Quarkly; 18.02.2014
comment
Это решение предлагает только статический конструктор: нет доступа к this. - person Cœur; 08.08.2014
comment
Небольшое упрощение, вместо class MyCustomization вам нужно только объявить Task _customization = TaskEx.Run(async () => { /* Do customization */ });. И async можно опустить, если он вам не нужен. - person Cœur; 12.08.2014

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

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

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

person rrreee    schedule 27.09.2011
comment
Большая проблема в том, что автоматически сгенерированный код должен реализовывать это, но во многих случаях я не могу контролировать код автогенерации. - person VoteCoffee; 14.05.2014

Проблема с 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.

person Doctor Jones    schedule 17.06.2013

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

public partial class MyClass{ 

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

    public partial void OnCreated();
}

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

РЕДАКТИРОВАТЬ:

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

person jonnii    schedule 29.10.2008

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

Изменить: это то же самое и тоже выглядит интересно.

person cfeduke    schedule 29.10.2008

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

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

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

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

SomeClass objSomeClass = new SomeClass(0);
person Shadi    schedule 14.02.2012

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

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

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

}

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

person nreyntje    schedule 30.12.2010
comment
Разрешение использования ключевого слова partial в конструкторах с той же сигнатурой может быть сделано, чтобы указать компилятору, что методы на самом деле предназначены для того, чтобы быть одним конструктором, и весь содержащийся код запускается в негарантированном порядке. - person N-ate; 12.07.2021

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

using System.Runtime.Serialization;

partial class MyWebService
{
     [OnDeserialized]
     public void OnDeserialized(StreamingContext context)
     {
         // your code here
     }
}
person Edward    schedule 22.02.2011
comment
Это для веб-службы или для объектов, десериализуемых по мере их возврата в вызове службы? Я попытался добавить его в свой частичный класс клиента веб-службы, но мой метод не вызывается ... - person Adriaan Davel; 24.11.2011

Я не совсем обращаюсь к OP, но если вам случится создавать классы с помощью EntityFramework Reverse POCO Generator, в конструкторе вызывается частичный метод, который удобен для инициализации вещей, которые вы добавляете через частичные классы самостоятельно. .

Создано инструментом:

   [System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.37.3.0")]
    public partial class Library {
        public string City { get; set; }
        public Library() {
            InitializePartial();
        }
        partial void InitializePartial();
    }

добавлено вами:

    public partial class Library {
        List<Book> Books { get; set; }
        partial void InitializePartial() {
            Books = new List<Book>();
        }
    }

    public class Book {
        public string Title { get; set; }
    }
person msulis    schedule 18.09.2019

Ничего такого, что я могу придумать. «Лучший» способ, который я могу придумать, - это добавить 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);
person James Curran    schedule 29.10.2008