Cómo utilizar el método de ocultación (nuevo) con la clase genérica constreñido

StackOverflow https://stackoverflow.com/questions/2649514

  •  27-09-2019
  •  | 
  •  

Pregunta

I tiene una clase de contenedor que tiene un parámetro genérico que está constreñido a alguna clase base. El tipo suministrado al genérico es un sub de la restricción de clase base. El escondite método utiliza subclase (nuevo) para cambiar el comportamiento de un método de la clase base (no, no puedo hacer lo virtual ya que no es mi código). Mi problema es que los 'nuevos' métodos no se les llama, el compilador parece considerar el tipo suministrado para que la clase base, no el submarino, como si tuviera conversión hacia arriba a la base.

Es evidente que estoy entendiendo mal algo fundamental aquí. Pensé que el where T: xxx genérico era una limitación, no un tipo de conversión hacia arriba.

Este código de ejemplo demuestra básicamente lo que estoy hablando.

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

Editar

Creo que mi confusión proviene de que uno puede hacer esto

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

Y que esperaba T ser T a pesar de que entiendo el compilador ver T como tener la interfaz de AnotherType. Asumí la tipificación de T ocurriría en tiempo de ejecución, incluso si la interfaz de T se fijó en tiempo de compilación. La declaración T foo parece engañosa aquí porque está haciendo realmente

AnotherType foo = new T();

Una vez que entiendo que no es realmente declarando foo tipo T, es comprensible por qué el método de ocultación new no funcionaría.

Y eso es todo lo que tengo que decir al respecto.

¿Fue útil?

Solución

Métodos declarada new tiene ninguna relación (desde la perspectiva del compilador) a los métodos con el mismo nombre / firma en la clase base. Esto es simplemente la manera del compilador de que le permite definir los diferentes métodos en derivados clases que comparten una firma con un método en su jerarquía de clase base.

Ahora, con respecto a su caso específico, se dan cuenta que los genéricos tienen que compilar a un único conjunto de código de bytes, independientemente de los tipos que se suministran como parámetros genéricos . Como resultado, el compilador sólo conoce el método y las propiedades que se definen en el tipo genérico T - que sería el tipo de base se especifica en la restricción genérica. El compilador no sabe nada acerca de los métodos new en su tipo derivado, incluso si se crea una instancia de un tipo genérico con el tipo derivado como parámetro. Pide, por consiguiente, en la clase genérica siempre ir a los métodos de la clase base.

Hay una gran confusión acerca de la nueva / / anulación virtual; tomar un vistazo a esta cuestión de forma - Jason y respuestas de Eric son excelentes. respuesta de Jon Skeet a una pregunta similar también puede ayudar a entender por qué su aplicación se comporta de la manera que lo hace.

Hay dos maneras posibles para que pueda trabajar alrededor de este problema:

  1. Realizar un elenco condicional (en base a información de tipo en tiempo de ejecución) para el tipo derivado (o una interfaz) en su clase genérica. Esto rompe la encapsulación y añade acoplamiento indeseable. También es frágil si se aplica mal.
  2. Definir una interfaz que se utiliza en su limitación genérica que expone los métodos que le interesan. Esto puede no ser posible si el código que está derivando a partir no es algo que se puede cambiar.

Otros consejos

esta pregunta SO pregunté pueden ayudarle. Véase la respuesta de Jon Skeet allí por qué no es posible.

Añadir otra capa - hereda su genérico no de su clase de tercero, sino de una nueva clase que a su vez hereda de la tercera parte. En esta nueva clase puede definir el método en cuestión como nueva virtual. Si todo el código nunca se hace referencia a la tercera clase parte directamente, se debe trabajar

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top