Question

J'ai une classe de conteneur qui a un paramètre générique qui est contraint à une classe de base. Le type fourni au générique est un sous de la contrainte de classe de base. Les utilisations de sous-classe méthode cache (nouveau) pour changer le comportement d'une méthode de la classe de base (non, je ne peux pas faire virtuelle car il n'est pas mon code). Mon problème est que les « nouvelles » méthodes ne sont pas appelés, le compilateur semble considérer le type fourni à la classe de base, et non le sous, comme si je devais upcast à la base.

Il est clair que je suis malentendu quelque chose de fondamental ici. Je pensais que le where T: xxx générique était une contrainte, pas un type upcast.

Ce code exemple montre essentiellement ce que je parle.

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

Modifier

Je suppose que ma confusion vient que l'on peut le faire

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

Je pensais T être T même si je comprends le compilateur considérerait T comme ayant l'interface de AnotherType. Je suppose la saisie de T se passerait-il à l'exécution même si l'interface de T a été fixé au moment de la compilation. La déclaration de T foo semble induire en erreur ici parce qu'il fait vraiment

AnotherType foo = new T();

Une fois que je comprends qu'il ne soit pas vraiment foo déclare que le type T, il est compréhensible que la dissimulation de la méthode de new ne fonctionnerait pas.

Et c'est tout ce que je dois dire à ce sujet.

Était-ce utile?

La solution

Méthodes déclarées new ont aucune relation (du point de vue du compilateur) aux méthodes avec le même nom / signature dans la classe de base. Ceci est simplement la manière du compilateur vous permettant de définir différentes méthodes dérivées classes qui partagent une signature avec une méthode dans leur classe de base. heirarchy

Maintenant, en ce qui concerne votre cas spécifique, se rendre compte que les génériques doivent compiler un ensemble unique de bytecode quels que soient les types qui sont fournis en tant que paramètres génériques . Par conséquent, le compilateur ne connaît que sur la méthode et les propriétés qui sont définies sur le type générique T - qui serait le type de base que vous spécifiez dans la contrainte générique. Le compilateur ne sait rien sur les méthodes de new dans votre type dérivé, même si vous créez une instance d'un type générique avec le type dérivé comme paramètre. demande par conséquent dans la classe générique sera toujours aller aux méthodes du type de base.

Il y a beaucoup de confusion au sujet du nouveau / override / virtuel; prendre réponse Jon Skeet à une question similaire peut également vous aider à comprendre pourquoi votre la mise en œuvre se comporte comme il le fait.

Il y a deux façons possibles pour vous de travailler autour de cette question:

  1. Effectuer une distribution conditionnelle (en fonction des informations de type d'exécution) du type dérivé (ou une interface) dans votre classe générique. Cette encapsulation et ajoute un couplage indésirable pauses. Il est également fragile si elle est appliquée mal.
  2. Définir une interface que vous utilisez dans votre contrainte générique qui expose les méthodes qui vous passionnent. Cela peut ne pas être possible si le code que vous dérivez de n'est pas quelque chose que vous pouvez changer.

Autres conseils

Je pense que cette question de SO j'ai demandé peut vous aider. Voir la réponse de Jon Skeet là pour pourquoi il est impossible.

Ajoutez une autre couche - Hériter votre générique pas de votre classe d'un tiers, mais d'une nouvelle classe qui hérite à son tour du tiers. Dans cette nouvelle classe, vous pouvez définir la méthode en question en tant que nouveau virtuel. Si tout votre code fait référence à jamais la classe troisième partie directement, il devrait fonctionner

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top