web-dev-qa-db-ja.com

基本型を派生型にキャストする方法

これが奇妙なことなのか、それともコードの匂いがするのかどうかはわかりませんが、「キャスト」する方法があるかどうか(疑問です)基本型から派生型の形式へ。派生型には、親が提供しない追加機能があり、それ自体は基本的に健全ではないため、これはほとんど意味をなさないことを知っています。しかし、これを行う方法はありますか?ここにコード例がありますので、私が尋ねていることをよりよく説明できます。

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());
}

これは間抜けなように思えますが、この種の何かを達成する方法はありますか?

45
Adam Driscoll

「マネージド」言語では適切ではありません。これはダウンキャスティングであり、説明したとおりの理由で、それを処理するための健全な方法はありません(サブクラスは基本クラス以上のものを提供します-この「もっと」はどこから来ますか?)。特定の階層に対して同様の動作が本当に必要な場合は、基本型をプロトタイプとして使用する派生型のコンストラクターを使用できます。

単純なケース(追加状態を持たない、より具体的なタイプ)を処理するリフレクションを使用して何かを構築できます。一般に、問題を回避するために再設計するだけです。

編集:おっと、ベース/派生型間の変換演算子を書くことはできません。 Microsoftが自分から「あなたを保護」しようとするのは奇妙です。ああ、少なくとも彼らはSunほど悪くない。

31
Adam Wright

継承の代わりに構成を試してください!

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がFruitの剥離の実装を再利用する代替方法を提供します。 Appleは、Fruitを拡張する代わりに、Fruitインスタンスへの参照を保持し、Fruitで単に剥離を呼び出す独自の剥離メソッドを定義できます。

18

個人的には、この場合に継承を使用する手間をかける価値はないと思います。代わりに、コンストラクターで基本クラスインスタンスを渡し、メンバー変数を介してアクセスします。

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
        {
        }
    }
}
15
Rob Sedgwick

派生クラスのオブジェクトはあるが、基本クラスタイプの参照によって参照され、何らかの理由で派生クラスタイプの参照によって参照されるようにしたい場合、ダウンキャストは理にかなっています。つまり、ダウンキャストして前のアップキャストの効果を逆にすることができます。ただし、派生クラス型の参照によって参照される基本クラスのオブジェクトを持つことはできません。

9
Maciej Hehl

私はこれをお勧めすると言っているわけではありません。ただし、基本クラスをJSON文字列に変換してから、派生クラスに変換することはできます。

SomeDerivedClass layer = JsonConvert.DeserializeObject<SomeDerivedClass>(JsonConvert.SerializeObject(BaseClassObject));
7
Donny V.

いいえ、これは不可能です。 C#のようなマネージ言語では、機能しません。コンパイラーが許可しても、ランタイムは許可しません。

これは間抜けだと自分自身に言った:

SomeBaseClass class = new SomeBaseClass();
SomeDerivedClass derClass = (SomeDerivedClass)class; 

classは実際にはSomeDerivedClassのインスタンスですか?いいえ、変換は意味がありません。 SomeBaseClassSomeDerivedClassに変換する必要がある場合は、コンストラクターまたは変換メソッドのいずれかの種類の変換を提供する必要があります。

ただし、クラス階層に何らかの作業が必要なように聞こえます。一般的に、基本クラスインスタンスを派生クラスインスタンスに変換することは可能ですすべきではありません。一般に、基本クラスに適用されないデータや機能が存在する必要があります。派生クラスの機能がベースクラスのallインスタンスに適用される場合、ベースクラスにロールアップするか、ベースクラス階層の一部ではない新しいクラスにプルする必要があります。

5
Derek Park

C#言語ではそのような演算子は許可されていませんが、それらを記述することはできますが、動作します。

[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Implicit(Base a) { ... }

[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Explicit(Base a) { ... }
5
Ark-kun

はい-これはコードの匂いであり、継承チェーンが壊れているという事実をほとんど特定しています。

私のguess(限られたサンプルから)は、DerivedClassがSomeBaseClassのインスタンスで動作するようにしたいということです-そのため、「DerivedClass」ではなく、「DerivedClass has SomeBaseClass」 SomeBaseClass」。これは「継承よりもお気に入りの構成」として知られています。

1
Mark Brackett

現在、基本クラスと派生クラスの両方が実装するインターフェイスについて考えましたか?この方法を実装する理由の詳細はわかりませんが、うまくいくかもしれません。

1
marr75

他の人が指摘したように、あなたが提案するキャストは実際には不可能です。 デコレーターパターン (ヘッドファーストエキス)を導入できる場合もあるでしょうか?

1
Boris Callens

派生クラスが持っている「余分な」ものをどのように取得するのか、これは不可能です。コンパイラーは、インスタンス化するときに、派生クラス2ではなく、派生クラス1を意味することをどのように知るでしょうか?

あなたが本当に探しているのはファクトリーパターンなどであるため、インスタンス化されている明示的な型を実際に知らなくてもオブジェクトをインスタンス化できます。あなたの例では、「Insert」メソッドを持つことは、ファクトリが返すインスタンスが実装するインターフェースになります。

0
Laplie Anderson

多くの答えが指摘しているように、あなたは完全に理にかなっているダウンキャストすることはできません。

ただし、あなたの場合、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>();

これは、基本クラスのすべてのデータが読み取りおよび書き込み可能なプロパティの形式である場合にのみ実際に機能します。

0
johnnycardy

なぜ誰もこれを言っていないのか分かりません、私は何かを見逃しているかもしれませんが、asキーワードを使用することができ、ifステートメントを使用する必要がある場合はifを使用します。

SomeDerivedClass derClass = class as SomeDerivedClass; //derClass is null if it isnt SomeDerivedClass
if(class is SomeDerivedClass)
    ;

-編集- 私はずっと前にこの質問をしました

0
user34537

それは機能しません。コンパイルエラーによってリンクされたヘルプページを見てください。

最良の解決策は、ここでファクトリメソッドを使用することです。

0
leppie

これはダウンキャストと呼ばれ、「安全な」バージョンを使用するというセルデクの提案は適切です。

かなりまともな コードサンプルの説明 です。

0
Ben Hoffstein

最近、いくつかのプロパティを追加するために、派生型で単純な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が非常に簡単になります。

0
lbergnehr