web-dev-qa-db-ja.com

C#で派生した戻り値の型を使用して抽象プロパティをオーバーライドする

私には4つのクラスがあります。 Request、DerivedRequest、Handler、DerivedHandler。 Handlerクラスには、次の宣言を持つプロパティがあります。

public abstract Request request { get; set; }

DerivedHandlerは、代わりにDerivedRequestを返すように、このプロパティをオーバーライドする必要があります。

public override DerivedRequest request { get; set; }

誰かがこれを機能させる方法について何かアイデアがありますか?

31
Trevor

これは、物事を構造化するための実際には良い方法ではありません。次のいずれかを実行します

1)戻り値の型を変更せず、サブクラスで通常どおりオーバーライドします。 DerivedHandlerでは、DerivedRequestの基本クラスシグネチャを使用してRequestのインスタンスを返すことができます。これを使用するクライアントコードは、必要に応じてDerivedRequestにキャストすることを選択できます。

2)ポリモーフィックであると想定されていない場合は、代わりにジェネリックを使用します。

public abstract class HandlerBase<T> where T: Request
{
    public abstract T Request {get;set;}
}

public class Handler: HandlerBase<Request>()

public class DerivedHandler: HandlerBase<DerivedRequest>()
18
Jamie Treworgy

C#言語では、同じ名前の別のメソッドに置き換えない限り、継承されたメソッドのシグネチャを変更することはできません。この手法は、「メンバーの非表示」または「シャドウイング」と呼ばれます。

.NET 2.0以降を使用している場合は、Requestプロパティの戻り値の型をHandlerクラスのジェネリック型パラメーターに変換することでこの問題を解決できます。次に、DerivedHandlerクラスは、その型パラメーターの引数としてDerivedRequestクラスを指定します。

次に例を示します。

// Handler.cs
public class Handler<TRequest> where TRequest : Request
{
    public TRequest Request { get; set; }
}

// DerivedHandler.cs
public class DerivedHandler : Handler<DerivedRequest>
{
}
6

元のプロパティを非表示にすることを除いて:

public new DerivedRequest Request { get;set;}

しかし、私はそれに対して強くお勧めします。オーバーライドされるはずの何かを非表示にすることは、特にプロパティが単純な自動生成されたものでない場合、問題を招きます。また、インターフェースまたは基本クラスとして使用する場合は、元の実装(その場合、継承ツリーの1つ上のクラス)。抽象クラスまたはインターフェースを実装している場合は、実装する必要があるため、元の署名を非表示にすることもできません。

通常、newキーワードの使用を検討している場合は、間違った方向に進んでいます。必要な場合もありますが、ほとんどの場合そうではありません。

代わりに、別のプロパティを作成します。

public DerivedRequest DerivedRequest {/* make adequate conversions here*/ }

そうすれば、あなたはOOPに関して明確な側にいて、あなたは明確な方法であなたの情報を得ることができます。

5
Femaref

編集:派生型の型を変更することはできませんが、newが役立つ場合があります。

派生型では...

public new DerivedRequest request
{
   get{return (DerivedRequest) base.request;}
   set{base.request = value;}
}
public override Request request
{
   get{return base.request;}
   set{base.request = (DerivedRequest) value;} // Throws InvalidCastException if misused.
}
1
agent-j
public class Request{}

public class DerivedRequest : Request{}

public class Handler<T>
  where T : Request
{
  public abstract T Request { get; set; }
}

public class DerivedHandler : Handler<DerivedRequest>
{
  public override DerivedRequest Request { get; set; }
}
0
agent-j

これは理論的には不可能です。オーバーライドは、戻り値の型に対して共変である必要があり(つまり、戻り値の型がより具体的または同じである必要があります)、パラメーターに対して反変である必要があります(つまり、パラメーターの型がより具体的でないか同じである必要があります)。したがって、新しい型は、Requestに関して事実上同時に共変および反変である必要があります。つまり、可能な型はRequestだけです。

このため、C#ではオーバーライドのプロパティの種類を変更することは許可されていません。

0
Vlad