Как переопределить метод в базовом классе, когда у нас есть более общий метод в базовом классе? - PullRequest
2 голосов
/ 20 января 2012

У меня есть базовый класс с именем Base.Этот базовый класс имеет метод, определенный как:

protected virtual string GetFormattedAttribute(string propertyName, object propertyValue)

propertyValue определяется как:

dynamic propertyValue

Таким образом, ему присваивается другой тип значений, и соответствующие методы вызываются автоматически,Например, у меня есть другой виртуальный метод в том же базовом классе для DateTime, как:

protected virtual string GetFormattedAttribute(string propertyName, DateTime propertyValue)

Это работает отлично.Теперь у меня есть класс dervied, в котором я определяю тот же метод, который принимает List<Contact>:

protected string GetFormattedAttribute(string propertyName, List<Contact> contacts)

Это, однако, никогда не вызывается, и первый метод с propertyValue как objectвместо этого вызывается Base (я вызываю этот метод с использованием объекта производного класса).Я пробовал ключевые слова new и override в методе производного класса, но это не работает.Есть идеи, как мне достичь этого результата?

Ответы [ 2 ]

3 голосов
/ 20 января 2012

Это не имеет ничего общего с переопределением.Вы создаете перегрузку из GetFormattedAttribute.Если вы вызываете GetFormattedAttribute для переменной типа Base, она не знает о перегрузке в производном классе, даже если экземпляр фактически имеет производный тип.

Проще говоря:Ваша перегрузка в производном классе будет вызываться только из-за того, что она вызывается для переменной производного класса или класса, производного от него.Поскольку метод защищен, я предполагаю, что вы вызываете GetFormattedAttribute где-то в вашем базовом классе.В этом сценарии невозможно использовать перегруженный GetFormattedAttribute в производном классе.

Один из способов достижения результата, который вы ищете, будет выглядеть примерно так:

  1. переопределить версию с параметром object в вашем производном классе
  2. поставить проверку типа в этой переопределенной версии.Если тип List<Contact>, вызовите эту перегрузку, в противном случае вызовите базовую реализацию

Примерно так:

class Derived : Base
{
    protected override string GetFormattedAttribute(string propertyName,
                                                    object propertyValue)
    {
        var list = propertyValue as List<Contact>;
        if(list == null)
            return base.GetFormattedAttribute(propertyName, propertyValue);
        else
            return GetFormattedAttribute(propertyName, list);
    }

    protected string GetFormattedAttribute(string propertyName,
                                           List<Contact> contacts)
    {
        // ...
    }
}
1 голос
/ 20 января 2012

Мне кажется, что вы можете использовать дженерики для достижения того, что вы хотите.

Так что в базе у вас будет

protected virtual string GetFormattedAttribute<T>(string propertyName, T propertyValue)

Это было бы полезно, если бы у всех типов было какое-то общее общее форматирование.

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

    public interface IPropertyFormatter<T> {
        string FormatValue(T value);
    } 

Наиболее универсальный класс, который потребуется для реализации этого интерфейса, - это просто когда вы делаете для Object.ToString() значение, поэтому у нас есть ObjectPropertyFormatterкак наиболее общая реализация IPropertyFormatter

    public class ObjectPropertyFormatter :  IPropertyFormatter<Object>
    {
        public string FormatValue(Object value)
        {
            //object fallback formatting logic 
            return  value.ToString();
        }
    }

Теперь предположим, что некоторые типы требуют специальной обработки.Затем мы реализуем специальные средства форматирования свойств для них.Поэтому вместо того, чтобы иметь один класс с тоннами перегрузок для всех конкретных случаев, у вас есть выделенные классы, которые обрабатывают логику форматирования.В этом примере есть DateTimePropertyFormatter и BooleanPropertyFormatter, и они будут реализованы следующим образом:

    public class DateTimePropertyFormatter :  IPropertyFormatter<DateTime> 
    {
        public string FormatValue(DateTime value)
        {
            //DateTime customised formatting logic 
            return "<b>" + value.ToString("yyyyMMdd") + "</b>";
        }
    }

    public class BoolPropertyFormatter :  IPropertyFormatter<bool>
    {
        public string FormatValue(bool value)
        {
            //bool customised formatting logic 
            if (value) 
                return "yeaaah"; 
            else 
                return "nope";
        }
    }

У вас может быть много других классов, таких как List и т. Д., Каждый со своей логикой форматированиясоблюдение принципа единой ответственности

Правильно, поэтому у нас есть наши средства форматирования, как нам заставить работать все наши средства форматирования?Это где FormatterResolver вступает в игру.Вы можете зарегистрировать средства форматирования, и они будут

    /// <summary>
    /// Class responsible for getting the right format resolver for a given type
    /// </summary>
    public class FormatterResolver
    {
        private ObjectPropertyFormatter _objectPropertyFormatter;

        private Dictionary<Type, object> _registeredFormatters;

        public FormatterResolver()
        {
            _registeredFormatters = new Dictionary<Type, object>();
            _objectPropertyFormatter = new ObjectPropertyFormatter();
        }

        public void RegisterFormatter<T>(IPropertyFormatter<T> formatter)
        {
            _registeredFormatters.Add(typeof(T), formatter);
        }

        public Func<string> GetFormatterFunc<T>(T value)
        {
            object formatter;
            if (_registeredFormatters.TryGetValue(typeof(T), out formatter))
            {
                return () => (formatter as IPropertyFormatter<T>).FormatValue(value);
            }
            else
                return () => ( _objectPropertyFormatter.FormatValue(value));

        }
    }

. Вам понадобится где-нибудь сохранить экземпляр formatResolver и зарегистрировать все средства форматирования.

    public FormatterResolver _formatResolver;

    public void RegisterFormatResolvers()
    {
        _formatResolver = new FormatterResolver();
        _formatResolver.RegisterFormatter(new BoolPropertyFormatter());
        _formatResolver.RegisterFormatter(new DateTimePropertyFormatter());
        //...etc

    }

Ваш метод будет выглядеть примерно так:

    public string GetFormattedAttribute<T>(T propertyValue)
    {
        return _formatResolver.GetFormatterFunc(propertyValue)();
    }

Итак, время проверить его, все ли работает?Это быстрый тест на исправность, который показывает, что приведенный выше код работает должным образом.

    [TestMethod]
    public void TestFormatResolvers()
    {
        RegisterFormatResolvers();

        Assert.AreEqual("yeaaah", GetFormattedAttribute(true));
        Assert.AreEqual("nope", GetFormattedAttribute(false));
        Assert.AreEqual("<b>20120120</b>", GetFormattedAttribute(new DateTime(2012, 01, 20)));
        Assert.AreEqual("5", GetFormattedAttribute(5));
    }

Если ваша логика форматирования также зависит от propertyName, все, что вам нужно сделать, это изменить интерфейс на:

    public interface IPropertyFormatter<T> {
        string FormatValue(string propertyName, T value);
    } 

и соответственно реализовать классы-потомки

...