Introspezione vs polimorfismo in C #

0

Mi chiedo quanto sia costosa l'introspezione di C # in termini di controllo del tipo di classe.

Diciamo che ho un problema, dove in un metodo di D devo decidere di agire a seconda del tipo di oggetti diversi B:A e C:A .

Ora potrei farlo con l'introspezione:

public class B: A {}

public class C: A {}

public class D
{
    void DoSomething()
    {
        A decisionCause = ...

        if(decisionCause.GetType() == typeof(B)) ReactToB();
        if(decisionCause.GetType() == typeof(C)) ReactToC();
    }
}

Questa è una violazione del principio aperto / chiuso? Altrimenti potrei anche fare B e C manipolare D e reagire a seconda di questa manipolazione:

public class A
{
    public abstract void Manipulate(A manipulating);
}

public class B: A
{
    public override void Manipulate(A manipulating)
    {
        // manipulate in B's way
    }
}


public class C: A
{
    public override void Manipulate(A manipulating)
    {
        // manipulate in C's way
    }
}

public class D
{
    void DoSomething()
    {
        if(m_manipulated == valueSetByB) ReactToB();
        if(m_manipulated == valueSetByC) ReactToC();
    }
}

Tuttavia, non voglio che questi A oggetti sappiano del tutto class D .

Potrei anche fare qualcosa del genere:

class A
{
    public readonly System.Type derivedType;
    A(System.Type derivedType)
    {
        this.derivedType = derivedType;
    }
}

class B: A
{
    B(): base(typeof(B)) {};
}

class C: A
{
    C(): base(typeof(C)) {};
}

public class D
{
    void DoSomething()
    {
        A decisionCause = ...

        if(decisionCause.derivedType == typeof(B)) ReactToB();
        if(decisionCause.derivedType == typeof(C)) ReactToC();
    }
}

Tuttavia, penso che sia una cattiva idea permettere una distinzione di tipo come questa, poiché quelli che si estendono potrebbero fare qualcosa che non è stato inteso da colui che ha implementato A.

L'introspezione è un modo ragionevole? C'è un altro modo in cui questo potrebbe essere affrontato?

    
posta BooleanAssange 13.11.2016 - 21:59
fonte

2 risposte

4

Is this a violation of the open/closed principle?

Sì, è praticamente l'esempio canonico ...

Is introspection a reasonable way?

No. Usando la riflessione per sbirciare nei tipi di runtime come questo è maleodorante fino all'estremo. Fare un controllo manuale dei tipi è un segno di fuoco sicuro che stai facendo qualcosa di inappropriato se hai bisogno di ricorrere a quello piuttosto che usare gli strumenti più naturali forniti dalla lingua.

Is there another way this could be approached?

Naturalmente. Il primo approccio migliore sarebbe quello di adattare il progetto in modo tale che non sia necessario questo genere di cose. Senza sapere quale problema tu debba affrontare, non posso offrire molti più consigli al riguardo.

Un'altra alternativa migliore è quella di non codificare questo genere di cose in tipi. Basta avere A , con qualche tipo di dati (o meglio ancora, un delegato / strategia) che ne differenzia l'uso. Anche questo può essere puzzolente, ma più spesso è un buon approccio ai problemi in cui si hanno "tipi di A" - molto meglio dell'ereditarietà.

    
risposta data 13.11.2016 - 22:06
fonte
2

Sono d'accordo con le risposte alle tue domande pubblicate da Telastyn, quindi non lo duplicherò. Vedo che stai cercando di risolvere un problema risolto da modello di strategia . La responsabilità di sapere come reagire in modo diverso è B o C, non D. Vuoi attivare un tipo, quindi eseguilo in modalità OO (Sei molto vicino a questo nel tuo secondo snippet):

            public interface IReactable
            {
                void React(A instance);
            }

            public class A : IReactable
            {
                public abstract void Manipulate(A manipulating);
            }

            public class B: A
            {
                public override void Manipulate(A manipulating)
                {
                    // manipulate in B's way
                }
                public void React(A reacting)
                {
                    // react in B's way
                }
            }


            public class C: A
            {
                public override void Manipulate(A manipulating)
                {
                    // manipulate in C's way
                }
                public void React(A reacting)
                {
                    // react in C's way
                }
            }

            public class D
            {
                public D(A Ainstance) 
                {
    // Inject the dependency here. 
    // It can be B, C, or any future implementation, 
    // and it wont break the code (O & D from SOLID) 
                   instance = Ainstance;
                }
                private A instance;
                void DoSomething()
                {
    // Core of strategy pattern.
    // The 'instance' implementation should know, how to react. 
                    A decisionCause = something;
                    instance.React(decisionCause);
                }
            }

E come ha detto Telastyn, in questo caso la riflessione è un grande odore di codice.

    
risposta data 13.11.2016 - 23:31
fonte

Leggi altre domande sui tag