基本型を派生型にキャストする方法
-
02-07-2019 - |
質問
これが奇妙なことなのかどうか、それともコードの匂いなのかはわかりません...しかし、「キャスト」する方法 (ある種の oop パターンがあれば便利です) があるのではないかと考えていました。基本型をその派生型の形式に変換します。派生型には親が提供していない追加機能があり、それ自体は基本的に健全ではないため、これがほとんど意味がないことはわかっています。しかし、これを行う方法はあるのでしょうか?ここにコード例を示します。これにより、私が求めていることをよりよく説明できます。
public class SomeBaseClass {
public string GetBaseClassName {get;set;}
public bool BooleanEvaluator {get;set;}
}
public class SomeDerivedClass : SomeBaseClass {
public void Insert(SqlConnection connection) {
//...random connection stuff
cmd.Parameters["IsItTrue"].Value = this.BooleanEvalutar;
//...
}
}
public static void Main(object[] args) {
SomeBaseClass baseClass = new SomeBaseClass();
SomeDerivedClass derClass = (SomeDerivedClass)baseClass;
derClass.Insert(new sqlConnection());
}
これはばかげているように見えることはわかっていますが、この種のことを達成する方法はありますか?
解決
「管理された」言語では健全ではありません。これは ダウンキャスト, 、そして、まさにあなたが説明した理由により、それを処理するためのまともな方法はありません(サブクラスは基本クラス以上のものを提供します - この「以上」はどこから来たのでしょうか?)。特定の階層に対して同様の動作が本当に必要な場合は、基本型をプロトタイプとして受け取る派生型のコンストラクターを使用できます。
単純なケース (加算状態を持たないより具体的な型) を処理するリフレクションを使用して何かを構築することもできます。一般に、問題を回避するには再設計するだけです。
編集:おっと、基本型と派生型の間の変換演算子は作成できません。Microsoft が自分自身から「あなたを守ろう」とするのは奇妙なことです。ああ、少なくとも彼らはサンほどひどいわけではない。
他のヒント
継承ではなく合成を試してください。
SomeBaseClass のインスタンスを SomeDerivedClass に渡す方が良いように思えます (これは基本クラスを派生しないため、そのように名前を変更する必要があります)
public class BooleanHolder{
public bool BooleanEvaluator {get;set;}
}
public class DatabaseInserter{
BooleanHolder holder;
public DatabaseInserter(BooleanHolder holder){
this.holder = holder;
}
public void Insert(SqlConnection connection) {
...random connection stuff
cmd.Parameters["IsItTrue"].Value = holder.BooleanEvalutar;
...
}
}
public static void Main(object[] args) {
BooleanHolder h = new BooleanHolder();
DatabaseInserter derClass = new DatabaseInserter(h);
derClass.Insert(new sqlConnection);
}
チェックアウト http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html (3ページ):
構成を介したコードの再利用は、Appleが果物のPeel()の実装を再利用するための代替方法を提供します。フルーツを伸ばす代わりに、Appleは果物のインスタンスへの参照を保持し、単に果物にPeel()を呼び出す独自のPeel()メソッドを定義できます。
個人的には、この場合、わざわざ継承を使用する価値はないと思います。代わりに、基本クラスのインスタンスをコンストラクターに渡し、メンバー変数を通じてアクセスするだけです。
private class ExtendedClass //: BaseClass - like to inherit but can't
{
public readonly BaseClass bc = null;
public ExtendedClass(BaseClass b)
{
this.bc = b;
}
public int ExtendedProperty
{
get
{
}
}
}
派生クラスのオブジェクトがあり、それが基本クラス型の参照によって参照されており、何らかの理由でそれを派生クラス型参照によって参照されるようにしたい場合、ダウンキャストは意味があります。言い換えれば、ダウンキャストすることで、以前のアップキャストの効果を逆転させることができます。ただし、派生クラス型の参照によって参照される基本クラスのオブジェクトを持つことはできません。
これを勧めると言っているわけではありません。ただし、基本クラスを JSON 文字列に変換してから、それを派生クラスに変換することはできます。
SomeDerivedClass layer = JsonConvert.DeserializeObject<SomeDerivedClass>(JsonConvert.SerializeObject(BaseClassObject));
いいえ、それは不可能です。C# のようなマネージ言語では、それは機能しません。コンパイラがそれを許可しても、ランタイムはそれを許可しません。
あなた自身、これはばかげていると言いました:
SomeBaseClass class = new SomeBaseClass();
SomeDerivedClass derClass = (SomeDerivedClass)class;
だから自分自身に問いかけてください。 class
実際には~のインスタンス SomeDerivedClass
?いいえ、そのため変換には意味がありません。変換する必要がある場合 SomeBaseClass
に SomeDerivedClass
, の場合は、コンストラクターまたは変換メソッドのいずれかで、何らかの変換を提供する必要があります。
ただし、クラス階層には少し工夫が必要なようです。一般に、それは すべきではありません 基本クラスのインスタンスを派生クラスのインスタンスに変換できます。通常、基本クラスに適用されないデータや機能が存在するはずです。派生クラスの機能が適用される場合 全て 基本クラスのインスタンスである場合、それを基本クラスにロールアップするか、基本クラス階層の一部ではない新しいクラスにプルする必要があります。
C# 言語ではそのような演算子は許可されていませんが、それでも記述でき、機能します。
[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Implicit(Base a) { ... }
[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Explicit(Base a) { ... }
はい、これはコードの臭いであり、継承チェーンが壊れているという事実をほぼ特定します。
私の 推測 (限られたサンプルから) DerivedClass を SomeBaseClass のインスタンス上で動作させたいということです - そのため、「DerivedClass があります 「DerivedClass」ではなく「SomeBaseClass」 です SomeBaseClass」。これは「相続より有利な構成」として知られています。
他の人も指摘しているように、あなたが提案したキャストは実際には不可能です。もしかしたら、 デコレータパターン(Head First抜粋)を導入できますか?
現在の基本クラスと派生クラスの両方が実装するインターフェイスについて考えたことがありますか?なぜこのように実装しているのか詳細はわかりませんが、うまくいくかもしれません。
これはダウンキャストと呼ばれるもので、「安全な」バージョンを使用するという Seldaek の提案は妥当です。
ここはかなりまともです コードサンプルによる説明.
これは不可能です。なぜなら、派生クラスが持つ「余分な」ものをどうやって取得するのでしょうか。コンパイラは、インスタンス化するときに、派生クラス 2 ではなく派生クラス 1 を意味していることをどのようにして知るのでしょうか?
あなたが本当に探しているのは、インスタンス化される明示的な型を実際に知らなくてもオブジェクトをインスタンス化できるようにするためのファクトリパターンまたは類似のものだと思います。あなたの例では、「Insert」メソッドを持つことは、ファクトリが返すインスタンスが実装するインターフェイスになります。
なぜ誰もこれを言わなかったのかわかりませんし、何かを見逃しているかもしれませんが、as キーワードを使用でき、if ステートメントを実行する必要がある場合は if を使用できます。
SomeDerivedClass derClass = class as SomeDerivedClass; //derClass is null if it isnt SomeDerivedClass
if(class is SomeDerivedClass)
;
-編集- この質問はずっと前にしました
最近、さらにいくつかのプロパティを追加するために、単純な DTO を派生型で拡張する必要がありました。次に、内部データベース タイプから DTO まで、手持ちの変換ロジックを再利用したいと思いました。
私がそれを解決した方法は、次のように使用して、DTO クラスに空のコンストラクターを強制することでした。
class InternalDbType {
public string Name { get; set; }
public DateTime Date { get; set; }
// Many more properties here...
}
class SimpleDTO {
public string Name { get; set; }
// Many more properties here...
}
class ComplexDTO : SimpleDTO {
public string Date { get; set; }
}
static class InternalDbTypeExtensions {
public static TDto ToDto<TDto>(this InternalDbType obj) where TDto : SimpleDTO, new() {
var dto = new TDto {
Name = obj.Name
}
}
}
これにより、複雑な DTO に変換するときに、単純な DTO の変換ロジックを再利用できます。もちろん、他の方法で複合型のプロパティを入力する必要がありますが、単純な DTO の非常に多くのプロパティを使用すると、IMO の作業が非常に単純になります。
それは機能しません。コンパイル エラーにリンクされているヘルプ ページを参照してください。
最良の解決策は、ここでファクトリーメソッドを使用することです。
多くの回答が指摘しているように、ダウンキャストすることはできません。これは完全に理にかなっています。
ただし、あなたの場合、 SomeDerivedClass
「欠落」するプロパティはありません。したがって、次のような拡張メソッドを作成できます。
public static T ToDerived<T>(this SomeBaseClass baseClass)
where T:SomeBaseClass, new()
{
return new T()
{
BooleanEvaluator = baseClass.BooleanEvaluator,
GetBaseClassName = baseClass.GetBaseClassName
};
}
したがって、キャストではなく、単に変換するだけです。
SomeBaseClass b = new SomeBaseClass();
SomeDerivedClass c = b.ToDerived<SomeDerivedClass>();
これは、基本クラス内のすべてのデータが読み取りおよび書き込み可能なプロパティの形式である場合にのみ実際に機能します。
C++ はコンストラクターを使用してこれを処理します。 C++ の型キャスト. 。それは私には見落としのようです。皆さんの多くは、プロセスが追加のプロパティをどうするかという問題を提起しました。プログラマがプロパティを設定していない場合、コンパイラは派生クラスを作成するときに何を行うのでしょうか?私はこの状況を C++ と同様に処理しました。基本クラスを受け取るコンストラクターを作成し、コンストラクター内のプロパティを手動で設定します。これは、派生クラスに変数を設定して継承を破るよりも明らかに望ましいです。また、結果として得られるコードの見た目がきれいになると思うので、私はファクトリ メソッドよりもこれを選択します。