部分クラスのデフォルトコンストラクタを別の部分クラスでオーバーライド
-
05-07-2019 - |
質問
これは可能だとは思いませんが、可能であれば必要です:)
Visual Studio 2008のwsdl.exeコマンドラインツールから自動生成されたプロキシファイルがあります。
プロキシ出力は部分クラスです。生成されたデフォルトのコンストラクタをオーバーライドしたい。コードは自動生成されるので、修正したくないです。
別の部分クラスを作成してデフォルトのコンストラクタを再定義しようとしましたが、うまくいきません。次に、overrideキーワードと新しいキーワードを使用してみましたが、機能しません。
部分クラスから継承できることは知っていますが、それは新しい親クラスを指すようにすべてのソースコードを変更する必要があることを意味します。これをする必要はありません。
アイデア、回避策、またはハッキングはありますか?
//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...
}
}
}
解決
これは不可能です。 部分クラスは、本質的に同じクラスの一部です。メソッドを2回定義したりオーバーライドしたりすることはできず、コンストラクターが含まれます。
コンストラクタでメソッドを呼び出し、他のパートファイルでのみ実装できます。
他のヒント
同様の問題があり、生成されたコードはdbmlファイルによって作成されました(私はLinq-to-SQLクラスを使用しています)。
生成されたクラスでは、コンストラクターの最後でOnCreated()と呼ばれる部分的なvoidを呼び出します。
簡単に言えば、生成されたクラスが行う重要なコンストラクターを保持する場合(おそらく実行する必要があります)、部分クラスで次を作成します。
partial void OnCreated()
{
// Do the extra stuff here;
}
うーん、 エレガントなソリューションの1つは次のとおりだと思います:
//* 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
}
}
}
このアプローチにはいくつかの欠点があります(すべてとして):
-
AutogenClsクラスの構築手順全体でMyCustomization内部クラスのコンストラクターが正確にいつ実行されるかは不明です。
-
MyCustomizationクラスのアンマネージリソースの破棄を正しく処理するためにMyCustomizationクラスのIDiposableインターフェイスを実装する必要がある場合、MyCustomization.Dispose()メソッドをトリガーせずにAutogenCls.csファイルに触れる...(しかし、「まだ」と言ったように:)
しかし、このアプローチは自動生成コードからの優れた分離を提供します-カスタマイズ全体が異なるsrcコードファイルに分離されます。
お楽しみください:)
実際には、部分的なメソッドが追加されたため、これが可能になりました。これがドキュメントです:
http://msdn.microsoft.com/en-us/library/ wa80x488.aspx
基本的には、部分クラスを定義する1つのファイルでメソッドを宣言して呼び出すことはできますが、実際にはそのファイルでメソッドを定義することはできません。もう一方のファイルで、メソッドを定義できます。メソッドが定義されていないアセンブリをビルドしている場合、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が持っている問題は、Web参照プロキシが、コンストラクターのインターセプトに使用できる部分的なメソッドを生成しないことです。
同じ問題が発生しました。WCFにアップグレードすることはできません。対象のWebサービスが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;
}
これは自動生成コードのハッキングを伴わず、SoapHttpClientProtocol自動生成プロキシの初期化ログインを実行するOPの正確なユースケースに適合するため、これは素晴らしいソリューションです。
これはできません。定義を作成できる部分的な方法を使用することをお勧めします。次のようなもの:
public partial class MyClass{
public MyClass(){
... normal construction goes here ...
AfterCreated();
}
public partial void OnCreated();
}
残りは自明のはずです。
編集:
また、このサービスのインターフェイスを定義する必要があることを指摘したいと思います。このインターフェイスは、実際に実装するために参照する必要がないようにプログラミングできます。これを行った場合、他にもいくつかのオプションがあります。
PostSharp を使用してこれを実行できる可能性があると考えています。 生成された部分クラスのメソッドが欲しい。これがメソッドを書く能力に容易に変換され、まだショットを与えていないのでそのボディをコンストラクターに置き換えるかどうかはわかりませんが、ショットの価値があるようです。
編集:これは同じ行に沿っており、面白そうです。
これは私の意見では、言語の設計上の欠陥です。彼らは、1つの部分的なメソッドの複数の実装を許可する必要がありました。 さらに良い方法では、コンストラクター(メソッドでもある)を部分的にマークするだけで、オブジェクトの作成時に同じシグネチャを持つ複数のコンストラクターが実行されます。
最も簡単な解決策は、追加の部分クラスごとに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));
}
}
しかし、実際には、部分クラスを処理するために、さらにいくつかの言語構造を追加する必要があります。
Visual Studioによって生成されたWebサービスプロキシの場合、部分クラスに独自のコンストラクターを追加することはできません(できますが、呼び出されません)。代わりに、[OnDeserialized]属性(または[OnDeserializing])を使用して、Webプロキシクラスがインスタンス化されるポイントで独自のコードをフックできます。
using System.Runtime.Serialization;
partial class MyWebService
{
[OnDeserialized]
public void OnDeserialized(StreamingContext context)
{
// your code here
}
}
アクセスできない場合や、デフォルトのコンストラクターを変更できない場合があります。そのため、メソッドを呼び出すためにデフォルトのコンストラクターを使用することはできません。
この場合、ダミーパラメーターを使用して別のコンストラクターを作成し、この新しいコンストラクターで&quot ;: this()"
を使用してデフォルトコンストラクターを呼び出すことができますpublic SomeClass(int x) : this()
{
//Your extra initialization here
}
このクラスの新しいインスタンスを作成するときは、次のようなダミーパラメータを渡すだけです:
SomeClass objSomeClass = new SomeClass(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);