Pergunta

Eu tenho uma classe de contêiner que possui um parâmetro genérico que é restrito a alguma classe base. O tipo fornecido ao genérico é um sub da restrição de classe base. A sub -classe usa o Hiding (New) para alterar o comportamento de um método da classe base (não, não posso torná -lo virtual, pois não é o meu código). Meu problema é que os métodos 'novos' não são chamados, o compilador parece considerar o tipo fornecido como a classe base, não o sub, como se eu tivesse enviado -o para a base.

Claramente, estou entendendo mal algo fundamental aqui. Eu pensei que o genérico where T: xxx foi uma restrição, não um tipo de upcast.

Esse código de exemplo demonstra basicamente do que estou falando.

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:

Acho que minha confusão vem disso pode fazer isso

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

E eu esperava T ser T Mesmo que eu entenda o compilador veria T como tendo AnotherTypeinterface. Eu assumi a digitação de T aconteceria em tempo de execução, mesmo que a interface de T foi definido no tempo de compilação. o T foo A declaração parece enganosa aqui porque está realmente fazendo

AnotherType foo = new T();

Uma vez eu entendo que não está realmente declarando foo como tipo T, é compreensível por que o new O esconderijo do método não funcionaria.

E isso é tudo o que tenho a dizer sobre isso.

Foi útil?

Solução

Métodos declarados new Não tenha relação (da perspectiva do compilador) com os métodos com o mesmo nome/assinatura na classe base. Essa é simplesmente a maneira do compilador de permitir que você defina diferentes métodos em classes derivadas que compartilham uma assinatura com um método na herança da classe base.

Agora, com relação ao seu caso específico, perceba que os genéricos precisam compilar com um único conjunto de bytecode, independentemente dos tipos que são fornecidos como parâmetros genéricos. Como resultado, o compilador conhece apenas o método e as propriedades definidas no tipo genérico T - esse seria o tipo de base que você especificará na restrição genérica. O compilador não sabe nada sobre o new Métodos no seu tipo derivado, mesmo se você criar uma instância de um tipo genérico com o tipo derivado como parâmetro. Portanto, as chamadas na classe genérica sempre vão para os métodos do tipo base.

Há muita confusão sobre a nova/virtual/substituição; dê uma Veja isso, então pergunta - As respostas de Jason e Eric são excelentes. Resposta de Jon Skeet Para uma pergunta semelhante, também pode ajudá -lo a entender por que sua implementação se comporta da maneira que se trata.

Existem duas maneiras possíveis de você contornar esse problema:

  1. Realizar um elenco condicional (com base nas informações do tipo de tempo de execução) no tipo derivado (ou uma interface) em sua classe genérica. Isso quebra o encapsulamento e adiciona acoplamento indesejável. Também é frágil se implementado mal.
  2. Defina uma interface que você usa em sua restrição genérica que expõe os métodos de que você se preocupa. Isso pode não ser possível se o código da qual você derivar não for algo que você pode alterar.

Outras dicas

Eu penso isso é tão pergunta Eu perguntei que pode ajudá -lo. Veja a resposta de Jon Skeet por que não é possível.

Adicione outra camada - herde seu genérico não da sua aula de terceiros, mas de uma nova classe que, por sua vez, herda do terceiro. Nesta nova classe, você pode definir o método em questão como novo virtual. Se todo o seu código nunca referenciar diretamente a terceira parte da classe, ele deve funcionar

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top