web-dev-qa-db-ja.com

部分クラスのデフォルトコンストラクタを別の部分クラスでオーバーライド

私はこれが可能であるとは思わないが、もしそうなら私はそれが必要です:)

Visual Studio 2008のwsdl.exeコマンドラインツールから自動生成されたプロキシファイルがあります。

プロキシ出力は部分クラスです。生成されたデフォルトのコンストラクタをオーバーライドしたい。コードは自動生成されるため、コードを変更したくないです。

別の部分クラスを作成してデフォルトのコンストラクタを再定義しようとしましたが、うまくいきません。次に、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...
      }
   }
}
52
Elijah Manor

これは不可能です。部分クラスは、本質的に同じクラスの一部です。メソッドを2回定義したりオーバーライドしたりすることはできず、コンストラクターが含まれます。

コンストラクターでメソッドを呼び出し、他のパーツファイルでのみ実装できます。

38
configurator

生成されたコードはdbmlファイル(Linq-to-SQLクラスを使用しています)で作成されているため、同様の問題がありました。

生成されたクラスでは、コンストラクターの最後にOnCreated()と呼ばれる部分的なvoidを呼び出します。

簡単に言えば、生成されたクラスが行う重要なコンストラクターを保持する場合(おそらく実行する必要があります)、部分クラスで次を作成します。

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

うーん、私は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
        }
    }
}

このアプローチにはいくつかの欠点があります(すべてとして):

  1. AutogenClsクラスの構築手順全体で、MyCustomization内部クラスのコンストラクターが正確にいつ実行されるかは明確ではありません。

  2. MyCustomizationクラスのアンマネージリソースの破棄を正しく処理するためにMyCustomizationクラスのIDiposableインターフェイスを実装する必要がある場合、AutogenCls.csファイルに触れることなくMyCustomization.Dispose()メソッドをトリガーする方法は(まだ)わかりません...(しかし、「まだ」と言ったように:)

しかし、このアプローチは自動生成コードからの優れた分離を提供します-カスタマイズ全体が異なるsrcコードファイルに分離されます。

楽しい :)

13
user307938

OPが持っている問題は、Web参照プロキシが、コンストラクターをインターセプトするために使用できる部分的なメソッドを生成しないことです。

私は同じ問題に遭遇しましたが、ターゲットにしている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の正確なユースケースに適合するため、素晴らしいソリューションです。

3
Doctor Jones

実際には、部分メソッドが追加されたため、これが可能になりました。ドキュメントは次のとおりです。

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

それは多少制限されており、変更する必要がある生成されたファイルがあるこの特定の場合、それは正しい解決策ではないかもしれませんが、部分クラスの機能をオーバーライドしようとしてこれにつまずいた人は、これができますとても助かります。

3
rrreee

これはできません。定義を作成できる部分的な方法を使用することをお勧めします。何かのようなもの:

public partial class MyClass{ 

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

    public partial void OnCreated();
}

残りはかなり自明です。

編集:

また、このサービスのインターフェイスを定義する必要があることを指摘しておきます。このインターフェイスを使用してプログラムを作成できるため、実際の実装を参照する必要はありません。これを行った場合、他にもいくつかのオプションがあります。

2
jonnii

PostSharp でこれを行うことができるかもしれないと思っています。誰かがあなたのやったことをやったようです 生成された部分クラスのメソッドが欲しい 。まだショットを与えていないので、これがメソッドを書く能力に容易に変換され、そのボディがコンストラクターを置き換えるかどうかはわかりませんが、ショットの価値があるようです。

編集: これは同じ行に沿っています また、面白そうです。

2
cfeduke

アクセスできない場合や、デフォルトのコンストラクターを変更できない場合があります。そのため、メソッドを呼び出すためのデフォルトのコンストラクターを使用することはできません。

この場合、ダミーパラメータを使用して別のコンストラクタを作成し、この新しいコンストラクタを作成して、「:this()」を使用してデフォルトコンストラクタを呼び出すことができます。

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

そして、このクラスの新しいインスタンスを作成するときは、次のようなダミーパラメータを渡すだけです。

SomeClass objSomeClass = new SomeClass(0);
1
Shadi

私の意見では、これは言語の設計上の欠陥です。彼らは、ニースの解決策を提供する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));
    }

}

しかし、実際には、部分クラスを処理するために、さらにいくつかの言語構造を追加する必要があります。

1
nreyntje

私が考えることができるものは何もありません。私が思いつく「最良の」方法は、ダミーパラメータを持つ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);
0
James Curran

Visual Studioによって生成されたWebサービスプロキシの場合、部分クラスに独自のコンストラクターを追加することはできません(できますが、呼び出されません)。代わりに、[OnDeserialized]属性(または[OnDeserializing])を使用して、Webプロキシクラスがインスタンス化されるポイントで独自のコードをフックできます。

using System.Runtime.Serialization;

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