Расширение класса - лучшая практика / лучшее решение - PullRequest
2 голосов
/ 05 сентября 2010

Первое, что нужно отметить - я ЗНАЮ ШАБЛОНЫ ДЕЛЕГАЦИИ И ДЕКОРАТОРА!

Второе - я использую C # .NET 4.0, так что если вы придумали решение, специфичное для него, это нормально.Но если решение будет работать для любого языка и платформы ООП, это было бы замечательно.

И здесь возникает вопрос ...

У меня есть частичный класс (назовем его Class1), который я не могу изменить.Таким образом, я могу просто расширить его или / и наследовать от него.Этот класс предоставляет мне идеальную модель данных, единственное, что мне нужно, это добавить некоторые атрибуты к его свойствам (для проверки, определения значения текста метки в MVC и т. Д. - пока мне не нужны ответы типа «вы можете делать то, что вынужно без атрибутов », это не вопрос моего вопроса).

Нет проблем в использовании другого класса в качестве модели данных, поэтому я могу, скажем, создать Class2 : Class1 и использовать Class2 в качестве модели.Свойства, которые требуют атрибутов, будут определены как public new <type> <propertyname>.Это ограничит меня переписыванием только тех свойств, которые нуждаются в атрибутах, оставляя все остальные нетронутыми.

Меньшая проблема заключается в том, что мне не нужно переопределять методы получения и установки для свойств, поскольку все, что они будут содержать, это return base.<propertyname> и base.<propertyname> = value, и если таких свойств много, это означает много «глупого» кодирования.Есть ли способ избежать этого?

Большая проблема в том, что мне нужно параметризовать мой Class2 с экземпляром Class1 и сделать что-то вроде class2.<propertyname> = class1.<propertyname> для каждого отдельного свойства, которое у меня есть - слишком много "глупая кодировка.Я могу избежать этого, используя отражение - найдите все свойства с общедоступными методами получения и установки в Class1 и вызовите prop.SetValue(child, prop.GetValue(parent, null), null); в цикле.Это обеспечивает универсальную функцию для простых случаев, что вполне нормально, поскольку у меня в основном простые модели - множество свойств с открытыми геттерами и сеттерами без тела и другой логики.Но я хочу более общего решения, и я не люблю отражения.Любые идеи?

Вот полный код метода расширения, который создает Class2 на основе Class1

    public static Child ToExtendedChild<Parent, Child>(this Parent parent)
        where Child : Parent, new()
    {
        Child child = new Child();

        var props = typeof(Parent).GetProperties().Where(p => p.GetAccessors().Count() >= 2);

        foreach (var prop in props)
        {
            prop.SetValue(child, prop.GetValue(parent, null), null);
        }

        return child;
    }

(кстати, этот метод не может идеально реализовать мое решение, поэтому любые исправления также приветствуются)

Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 05 сентября 2010

Меньшая проблема не кажется большой проблемой. Может быть, я неправильно понимаю вопрос, но, если вы просто выводите подкласс, не должно быть никаких причин переопределять либо свойства, либо связанные с ними методы получения / установки.

Большая проблема может быть решена с помощью чего-то более простого. Использование отражения для инициализации многих объектов кажется немного дорогостоящим. Если вы имеете дело с классом, который в основном представляет собой большую сумку или свойства, возможно, вам следует сделать так, как будто вам нужен доступ к всем из этих свойств в любой конкретной ситуации. Вы упоминаете MVC и валидацию. Используется ли вся модель в методе контроллера, в котором выполняется валидация? Если нет, то почему бы не посмотреть на использование модели представления, которая предоставляет только те части, которые вам нужны в этом методе?

Ваш инициализатор рефлексии интересен, но если вы собираетесь делать много этого, вы можете потратить немного времени на Automapper . В противном случае, возможно, стоит рассмотреть возможность перехода от общего решения к чему-то, что просто решает проблему, то есть сопоставление свойств от экземпляра объекта к другому экземпляру производного объекта. Может быть, вы можете создать конструктор копирования в родительском классе и использовать его в своем производном классе?

public class Foo {
    public string PropOne { get; set; }
    public string PropTwo { get; set; }

    public Foo(string propOne, string propTwo) {
        PropOne = propOne;
        PropTwo = propTwo;
    }

    public Foo(Foo foo) {
        PropOne = foo.PropOne;
        PropTwo = foo.PropTwo;
    }
}

public class Pho : Foo {
    // if you have additional properties then handle them here
    // and let the base class take care of the rest.
    public string PropThree { get; set; }
    public Pho(string propOne, string propTwo, string propThree) 
        : base(propOne, propTwo) {
        PropThree = propThree; 
    }
    public Pho(Pho pho) : base(pho) {
        PropThree = pho.PropThree;
    }
    // otherwise you can just rely on a copy constructor
    // to handle the initialization.
    public Pho(Foo foo) : base(foo) {}
}
1 голос
/ 05 сентября 2010

Я полагаю, что частичный класс - это сгенерированный код, это наиболее логично для вашего сценария.

Я знаю один способ сделать это, но в зависимости от того, как атрибут сканируется, он может не работать.

// Generated Code
public partial Class1
{
  public string Foo { get { ... } }
}

// Your Code
public interface IClass1
{
  [MyAttribute]
  public string Foo { get; }
}

public partial Class1 : IClass1
{
}

Если бы кто-то смотрел на атрибуты, используя GetCustomAttributes с наследованием, то я думаю, что они получили бы этот атрибут.

В качестве отступления, всякий раз, когда я вижу сгенерированный код, который неу меня есть виртуальные свойства, это заставляет меня немного поплакать внутрь.

Чтобы ответить на ваш большой вопрос, почему бы вам просто не сделать Class2 оболочкой для Class1.Вместо того, чтобы копировать все свойства, вы можете просто дать Class2 экземпляр Class1 в конструкторе, сохранить его локально и сделать все ваши свойства сквозными.Это означает некоторое ручное кодирование, но если вы все равно создаете Class2 вручную и хотите украсить его набором атрибутов, ну, в любом случае, вы вручную кодируете Class2.

...