Domanda

Ho una classe contenitore che ha un parametro generico, che è costretto a qualche classe di base. Il tipo fornito al generico è un sub del vincolo classe base. Il metodo utilizza sub classe nascondiglio (nuovo) per modificare il comportamento di un metodo dalla classe base (no, non posso non rendono virtuale in quanto non è il mio codice). Il mio problema è che i metodi 'nuovi' non vengono chiamati, il compilatore sembra considerare il tipo in dotazione per essere la classe base, non il sub, come se avessi upcast alla base.

Chiaramente sto fraintendendo qualcosa di fondamentale qui. Ho pensato che il where T: xxx generica era un vincolo, non è un tipo di upcast.

Questo codice di esempio illustra fondamentalmente quello di cui sto parlando.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericPartialTest
{
    class ContextBase
    {
        public string GetValue()
        {
            return "I am Context Base: " + this.GetType().Name;
        }

        public string GetOtherValue()
        {
            return "I am Context Base: " + this.GetType().Name;
        }

    }

    partial class ContextSub : ContextBase
    {
        public new string GetValue()
        {
            return "I am Context Sub: " + this.GetType().Name;
        }
    }

    partial class ContextSub
    {
        public new string GetOtherValue()
        {
            return "I am Context Sub: " + this.GetType().Name;
        }
    }

    class Container<T> where T: ContextBase, new()
    {
        private T _context = new T();

        public string GetValue()
        {
            return this._context.GetValue();
        }

        public string GetOtherValue()
        {
            return this._context.GetOtherValue();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Simple");
            ContextBase myBase = new ContextBase();
            ContextSub mySub = new ContextSub();

            Console.WriteLine(myBase.GetValue());
            Console.WriteLine(myBase.GetOtherValue());
            Console.WriteLine(mySub.GetValue());
            Console.WriteLine(mySub.GetOtherValue());

            Console.WriteLine("Generic Container");
            Container<ContextBase> myContainerBase = new Container<ContextBase>();
            Container<ContextSub> myContainerSub = new Container<ContextSub>();

            Console.WriteLine(myContainerBase.GetValue());
            Console.WriteLine(myContainerBase.GetOtherValue());
            Console.WriteLine(myContainerSub.GetValue());
            Console.WriteLine(myContainerSub.GetOtherValue());


            Console.ReadKey();
        }
    }
}

Modifica

Credo che la mia confusione deriva dal che si può fare questo

class SomeClass<T> where T: AnotherType, new()
{
    T foo = new T();     
}

E mi aspettavo di essere T T anche se capisco il compilatore visualizzare T come avere l'interfaccia di AnotherType. Ho assunto la tipizzazione di T sarebbe successo in fase di esecuzione, anche se l'interfaccia di T è stato fissato in fase di compilazione. La dichiarazione T foo sembra fuorviante qui perché è davvero facendo

AnotherType foo = new T();

Una volta ho capito che non è davvero dichiarando foo come tipo T, è comprensibile il motivo per cui il metodo new nascondiglio non funzionerebbe.

E questo è tutto quello che ho da dire al riguardo.

È stato utile?

Soluzione

Metodi dichiarato new non hanno alcuna relazione (dal punto di vista del compilatore) per i metodi con lo stesso nome / firma nella classe base. Questo è semplicemente il modo del compilatore di permettere di definire metodi diversi in derivati classi che condividono una firma con un metodo nella loro gerarchia di classe di base.

Ora, per quanto riguarda il tuo caso specifico, rendersi conto che i farmaci generici devono compilare in un unico insieme di bytecode a prescindere dai tipi che vengono forniti come parametri generici . Di conseguenza, il compilatore conosce solo sul metodo e le proprietà che sono definiti sul tipo generico T - che sarebbe il tipo di base specificato nel vincolo generico. Il compilatore non sa nulla sui metodi new nel tipo derivato, anche se si crea un'istanza di un tipo generico con il tipo derivato come parametro. chiede pertanto nella classe generica sarà sempre andare ai metodi del tipo di base.

C'è un sacco di confusione sul nuovo / / Override virtuale; prendere un un'occhiata a questa domanda SO - Jason e le risposte di Eric sono eccellenti. di Jon Skeet risposta a una domanda simile può anche aiutare a capire il motivo per cui la vostra realizzazione si comporta il modo in cui lo fa.

Ci sono due modi possibili per voi per risolvere questo problema:

  1. Eseguire un cast condizionale (sulla base di informazioni di tipo runtime) per il tipo derivato (o un'interfaccia) nella classe generica. Questo pause incapsulamento e aggiunge accoppiamento indesiderabile. E 'anche fragile se attuate male.
  2. Definisci un'interfaccia che si utilizza nel vostro vincolo generico che espone i metodi che ti interessano. Questo potrebbe non essere possibile se il codice che si sta derivanti dal non è qualcosa che si può cambiare.

Altri suggerimenti

questa domanda SO ho chiesto può aiutare. La risposta di See Jon Skeet lì per il motivo per cui non è possibile.

Aggiungi un altro strato - ereditare il vostro generico non dalla vostra classe di terze parti, ma da una nuova classe che eredita in sua volta dal terzo. In questa nuova classe è possibile definire il metodo in questione come nuova virtuale. Se tutto il codice non fa riferimento direttamente alla terza classe di parte, dovrebbe funzionare

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top