Переопределить конструктор по умолчанию для частичного класса другим классом - PullRequest
51 голосов
/ 29 октября 2008

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

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

66 голосов
/ 09 января 2010

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

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

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

partial void OnCreated()
{
    // Do the extra stuff here;
}
37 голосов
/ 29 октября 2008

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

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

13 голосов
/ 03 апреля 2010

Ммм, Я думаю, что одно изящное решение было бы следующим:

//* 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. Если для класса MyCustomization будет необходимо реализовать интерфейс IDiposable для правильной обработки удаления неуправляемых ресурсов класса MyCustomization, я пока не знаю, как вызвать метод MyCustomization.Dispose () без прикосновение к файлу AutogenCls.cs ... (но, как я уже сказал "пока":)

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

наслаждайся:)

3 голосов
/ 17 июня 2013

Проблема, с которой столкнулся 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.

3 голосов
/ 27 сентября 2011

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

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

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

2 голосов
/ 29 октября 2008

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

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

2 голосов
/ 29 октября 2008

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

public partial class MyClass{ 

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

    public partial void OnCreated();
}

Остальное должно быть самоочевидным.

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

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

1 голос
/ 30 декабря 2010

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

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

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 голосов
/ 14 февраля 2012

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

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

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

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

SomeClass objSomeClass = new SomeClass(0);
0 голосов
/ 22 февраля 2011

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

using System.Runtime.Serialization;

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