web-dev-qa-db-ja.com

サブタイプのメソッドを呼び出すためのダウンキャスト

派生型のオブジェクトを含むリストを作成し、いずれかの型に固有のメソッドを呼び出す必要がある場合は、型チェックでフィルタリングすることをお勧めしますか?私が読んだことから、タイプチェックはある種のコードのにおいであり、OOPが完全には使用されていないことを示しています。

すべてのオブジェクトでいくつかの共通メソッドを呼び出したいので、すべてのサブタイプのオブジェクトに共通リストを使用します。ただし、一部のメソッドは特定のサブタイプに固有であり、そのメソッドを呼び出したい場合は、タイプでオブジェクトを除外する必要があります。

    public abstract class ParentType
    {
        public abstract void CommonMethod();
    }

    public class DerivedTypeA : ParentType
    {
        public override void CommonMethod()
        {
            //do something
        }

        public void TypeASpecificMethod()
        {
            //do something
        }
    }

    public class DerivedTypeB : ParentType
    {
        public override void CommonMethod()
        {
            //do something
        }

        public void TypeBSpecificMethod()
        {
            //do something
        }
    }

    static void Main()
    {
        List<ParentType> objects = new List<ParentType>()
        {
            new DerivedTypeA(),
            new DerivedTypeA(),
            new DerivedTypeB(),
            new DerivedTypeB()
        }

        //Call common method on all objects in a list
        foreach(var obj in objects)
        {
            obj.CommonMethod();
        }

        //Call only specific method for DerivedTypeA objects
        foreach(var obj in objects)
        {
            if(obj is DerivedTypeA derivedAObj) //type checking needed
            {
                derivedAObj.TypeASpecificMethod();
            }
        }
    }
1
kondziu

Aオブジェクトに対してprocessA()を、Bオブジェクトに対してprocessB()を呼び出す必要があるのはなぜですか?ほとんどの場合、動的にディスパッチされるメソッドprocess()を発明し、サブクラスを定義して、オーバーライドする各メソッドが自動的に適切な処理を行うようにすることで、より優れたソリューションになります。 (一部のメソッドが実際にすべてのサブクラスに適用されない場合、何もしないことは完全に適切な選択です。確かに、多くのtypeofチェックを使用してクライアントプログラムを作成するよりも優れています。)

3
Kilian Foth

それがコードのにおいであるかどうかはわかりませんが、特定の派生型をチェックし、その型で特定のメソッドを呼び出すというアプローチは、密結合された、保守が難しいコードにつながります。したがって、回避することをお勧めします。

その抽象クラスと派生クラスの代わりに、一連のインターフェースから各クラスを構成します。

public interface ICommon
{
    void CommonMethod();
}

public interface ITypeASpecific
{
    void TypeASpecificMethod();
}

public interface ITypeASpecific
{
    void TypeASpecificMethod();
}

public interface ITypeBSpecific
{
    void TypeBSpecificMethod();
}

public class TypeA : ICommon, ITypeASpecific
{
    public void CommonMethod()
    {
        //do something
    }

    public void TypeASpecificMethod()
    {
        //do something
    }
}

public class TypeB : ICommon, ITypeBSpecific
{
    public void CommonMethod()
    {
        //do something
    }

    public void TypeBSpecificMethod()
    {
        //do something
    }
}

次に、コードを特定のクラスに関連付けることなく、特定のアクションのタイプを確認できます。インターフェースだけです。また、このアプローチは、ASpecificとBSpecificの両方を実装する型が必要な状況を処理します。

public class TypeAB : ICommon, ITypeASpecific, ITypeBSpecific
{
    public void CommonMethod()
    {
        //do something
    }

    public void TypeASpecificMethod()
    {
        //do something
    }

    public void TypeBSpecificMethod()
    {
        //do something
    }
}

static void Main()
{
    var objects = new List<ICommon>()
    {
        new TypeA(),
        new TypeA(),
        new TypeB(),
        new TypeB(),
        new TypeAB()
    }

    foreach(var obj in objects)
    {
        obj.CommonMethod();

        if (obj is ITypeASpecific typeAObj) typeAObj.TypeASpecificMethod();
        if (obj is ITypeBSpecific typeBObj) typeBObj.TypeASpecificMethod();
    }
}
2
David Arno

派生型のオブジェクトを含むリストを作成し、いずれかの型に固有のメソッドを呼び出す必要がある場合は、型チェックでフィルタリングすることをお勧めしますか?私が読んだことから、タイプチェックはある種のコードのにおいであり、OOPが完全には使用されていないことを示しています。

polymorphism が完全には使用されていないことを示しています。ポリモーフィズムはOOPの重要な部分です。

確かに、ときどき行き詰まり、思い通りにできない場合がありますが、それを使用してこの問題を解決する方法を理解してください(可能な場合)。

なんらかの理由でこれらの派生型を単に変更できない場合でも、その型固有のチェックを廃止することができます。

static void Main()
{
    List<VagueType> objects = new List<VagueType>()
    {
        new VagueType( new DerivedTypeA() ),
        new VagueType( new DerivedTypeA() ),
        new VagueType( new DerivedTypeB() ),
        new VagueType( new DerivedTypeB() )
    }

    //Call common method on all objects in a list
    foreach(var obj in objects)
    {
        obj.CommonMethod();
    }

    //Call something, if needed
    foreach(var obj in objects)
    {
        obj.SomeSpecificMethod();
    }
}

public class VaugeType  
{
    public override void CommonMethod()
    {
        derived.CommonMethod();
    }

    //Call only specific method for DerivedTypeA objects
    public void SomeSpecificMethod()
    {
        if(derived is DerivedTypeA derivedAObj) //type checking needed
        {
            derivedAObj.TypeASpecificMethod();
        }
    }
}

お待ちください。タイプチェックだけを使用していませんか?はい、そうしました。しかし、私はそれを使用するアルゴリズムから遠ざけたので、使用するたびに醜いものを見ることを止めることができます。

型チェックが悪いことではありません。これは、コードの匂いであり、デザインとそれが引き起こしている問題について、立ち止まって考えることを促します。これは、醜い細部を引き寄せようとする新しいプログラマーの直感に反します。経験豊富なプログラマーは、詳細を取り除き、それらを背後に隠して、より重要なことについて考えることができるようにします。

2
candied_orange