Как использовать скрытие метода (новое) с общим ограниченным классом

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

  •  27-09-2019
  •  | 
  •  

Вопрос

У меня есть класс контейнера, который имеет общий параметр, который ограничен некоторым базовым классом.Тип, предоставляемый в generic, является подразделом ограничения базового класса.Подкласс использует скрытие метода (new) для изменения поведения метода из базового класса (нет, я не могу сделать его виртуальным, поскольку это не мой код).Моя проблема в том, что "новые" методы не вызываются, компилятор, похоже, рассматривает предоставленный тип как базовый класс, а не вспомогательный, как если бы я передал его в базовый.

Очевидно, я не понимаю здесь чего-то фундаментального.Я думал, что общий where T: xxx это было ограничение, а не повышающий тип.

Этот пример кода в основном демонстрирует то, о чем я говорю.

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

Редактировать:

Я думаю, мое замешательство происходит от того, что кто-то может это сделать

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

И я ожидал , что T быть T несмотря на то, что я понимаю, что компилятор будет просматривать T как имеющий AnotherTypeинтерфейс пользователя.Я предположил, что набрав T произойдет во время выполнения, даже если интерфейс T был установлен во время компиляции.Тот Самый T foo декларация здесь кажется вводящей в заблуждение, потому что она действительно делает

AnotherType foo = new T();

Как только я понимаю, что на самом деле это не объявление foo как тип T, понятно , почему new скрытие метода не сработало бы.

И это все, что я должен сказать по этому поводу.

Это было полезно?

Решение

Объявленные методы new не имеют никакого отношения (с точки зрения компилятора) к методам с таким же именем / сигнатурой в базовом классе. Это просто способ компилятора, позволяющий вам определять различные методы в производных классах, которые разделяют сигнатуру с методом в их иерархии базового класса.

Теперь, что касается вашего конкретного случая, поймите, что универсальные файлы должны компилироваться в один набор байт-кода независимо от типов, которые предоставляются в качестве универсальных параметров.В результате компилятор знает только о методе и свойствах, которые определены для универсального типа T - это будет базовый тип, который вы указываете в общем ограничении.Компилятор ничего не знает о new методы в вашем производном типе, даже если вы создаете экземпляр универсального типа с производным типом в качестве параметра.Поэтому вызовы в универсальном классе всегда будут обращаться к методам базового типа.

Существует много путаницы в отношении new / virtual / override; возьмите посмотрите на этот вопрос SO - Ответы Джейсона и Эрика превосходны. Ответ Джона Скита ответ на аналогичный вопрос также может помочь вам понять, почему ваша реализация ведет себя именно так.

Есть два возможных способа обойти эту проблему:

  1. Выполните условное приведение (на основе информации о типе среды выполнения) к производному типу (или интерфейсу) в вашем универсальном классе.Это нарушает инкапсуляцию и добавляет нежелательную связь.Он также хрупок, если плохо реализован.
  2. Определение интерфейса который вы используете в своем общем ограничении, предоставляющем методы, которые вас интересуют.Это может быть невозможно, если код, на основе которого вы выводите, не является чем-то, что вы можете изменить.

Другие советы

Я думаю , что это ТАКОЙ вопрос Я спросил, может ли это помочь вам.Смотрите там ответ Джона Скита о том, почему это невозможно.

Добавьте еще один уровень - унаследуйте свой generic не от вашего стороннего класса, а от нового класса, который, в свою очередь, наследуется от третьей стороны.В этом новом классе вы можете определить рассматриваемый метод как new virtual.Если весь ваш код никогда не ссылается напрямую на класс третьей части, это должно сработать

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top