Выделить класс без написания большого количества шаблонного кода?

Скажем, у меня есть сторонний класс, который представляет собой модель данных. Он имеет около 100 свойств (некоторые с общедоступными сеттерами и геттерами, другие с общедоступными геттерами, но частными сеттерами). Назовем этот класс ContosoEmployeeModel

Я хочу добавить к этому классу интерфейс (INavigationItem, у которого есть свойства Name и DBID), чтобы его можно было использовать в моем приложении (это поставщик PowerShell, но сейчас это не важно). Однако его также необходимо использовать как ContosoEmployeeModel.

Моя первоначальная реализация выглядела так:

public class ContosoEmployeeModel
{
    // Note this class is not under my control. I'm supplied
    // an instance of it that I have to work with.

    public DateTime EmployeeDateOfBirth { get; set; }
    // and 99 other properties.
}

public class FacadedEmployeeModel : ContosoEmployeeModel, INavigationItem
{
    private ContosoEmployeeModel model;
    public FacadedEmployeeModel(ContosoEmployeeModel model)
    {
        this.model = model;
    }

    // INavigationItem properties
    string INavigationItem.Name { get; set;}

    int INavigationItem.DBID { get; set;}

    // ContosoEmployeeModel properties
    public DateTime EmployeeDateOfBirth
    {
        get { return this.model.EmployeeDateOfBirth; }
        set { this.model.EmployeeDateOfBirth = value; }
    }
    // And now write 99 more properties that look like this :-(
}

Однако ясно, что это потребует написания огромного количества шаблонного кода для раскрытия всех свойств, и я бы предпочел этого избежать, если смогу. Я могу сгенерировать этот код в коде T4 в частичном классе, и сделаю это, если нет лучших идей, но я хотел бы попросить здесь посмотреть, есть ли у кого-нибудь лучшие идеи, используя некоторые super wizzy немного магии C #

Обратите внимание - API, который я использую для получения ContosoEmployeeModel, может возвращать только ContosoEmployeeModel - я не могу расширить его, чтобы вернуть FacededEmployeeModel, поэтому упаковка модели - единственное решение, которое я могу придумать - я рад, что меня исправят: )


person RB.    schedule 02.07.2013    source источник
comment
Как насчет использования AutoMapper?   -  person Matthew Watson    schedule 02.07.2013
comment
msdn.microsoft.com/en-us/library/fb3dyx26.aspx   -  person spender    schedule 02.07.2013
comment
Я запутался - вы наследуете свой класс фасада от ContosoEmployeeModel И у вас есть ContosoEmployeeModel, встроенный в ваш фасад через композицию? Почему?   -  person Treb    schedule 02.07.2013
comment
@MatthewWatson Ооо, я никогда об этом не слышал. Это выглядит очень многообещающе - я попробую и отправлю ответ   -  person RB.    schedule 02.07.2013
comment
Может быть, вы могли бы добавить свойство object к INavigationItem, которое, если не null, используется вместо INavigationItem, когда кто-то переходит к нему?   -  person dtb    schedule 02.07.2013
comment
Может, интерфейс создавать вообще не нужно? Если в вашем классе есть только данные и нет операций, чего вы добьетесь, введя интерфейс?   -  person Ilya Chernomordik    schedule 02.07.2013
comment
@Treb API, который я использую для сохранения / извлечения данных, принимает ContosoEmployeeModel. Однако приложение, которое управляет моделью (в данном случае PowerShell), принимает INavigationItem. Надеюсь, это ясно?   -  person RB.    schedule 02.07.2013
comment
@Spender Я не контролирую ContosoEmployeeModel, поэтому не могу его реорганизовать.   -  person RB.    schedule 02.07.2013
comment
@MatthewWatson Похоже, автоматическое сопоставление мне здесь не поможет - моя проблема в том, что я слишком ленив, чтобы полностью написать объект. Тем не менее, это хорошая вещь, о которой нужно знать, так что ура :)   -  person RB.    schedule 02.07.2013
comment
@RB. Ну что ж, выстрел стоил. :)   -  person Matthew Watson    schedule 02.07.2013
comment
@RB. : В этом случае функция делегирования Resharper - это именно тот дроид, который вам нужен. jetbrains.com/resharper/webhelp/ ... идеально подходит для создания фасадов / прокси.   -  person spender    schedule 02.07.2013


Ответы (2)


Другой подход может быть подходящим для вас - использовать AutoMapper для сопоставления базового класса с вашим фасадом, вот пример кода:

class Program
    {
        static void Main(string[] args)
        {
            var model = new Model { Count = 123, Date = DateTime.Now, Name = "Some name" };

            Mapper.CreateMap<Model, FacadeForModel>();
            var mappedObject = AutoMapper.Mapper.Map<FacadeForModel>(model);

            Console.WriteLine(mappedObject);

            Console.ReadLine();
        }

        class Model
        {
            public string Name { get; set; }

            public DateTime Date { get; set; }

            public int Count { get; set; }
        }

        interface INavigationItem
        {
            int Id { get; set; }

            string OtherProp { get; set; }
        }

        class FacadeForModel : Model, INavigationItem
        {
            public int Id { get; set; }

            public string OtherProp { get; set; }
        }
    }
person Alexandr Mihalciuc    schedule 02.07.2013
comment
К сожалению, для меня это не сработает - мне нужно, чтобы моя модель FacadedEmployeeModel имела те же значения для всех 100 свойств, что и ContosoEmployeeModel. - person RB.; 02.07.2013
comment
Для чего вы используете FacadedEmployeeModel? - person Alexandr Mihalciuc; 02.07.2013
comment
Из API я получаю модель Contoso. Однако мне нужно управлять этим как INavigationItem. Следовательно, фасад превратит модель Contoso в INavigationItem. - person RB.; 02.07.2013
comment
Используя AutoMapper, вы получите фасад, заполненный всеми данными из ContosoEmployeeModel, поэтому вы можете использовать его как INavigationItem и ContosoEmployeeModel одновременно, без необходимости повторно объявлять все свойства модели. - person Alexandr Mihalciuc; 02.07.2013
comment
Ты прав! Не могу поверить, что упустил этот момент! @MatthewWatson предложил использовать Automapper, но я, очевидно, отклонил его слишком быстро. К сожалению, в модели Contoso есть ошибка (свойство может быть инициализировано значением null, но не может иметь значение null), которую мне нужно изучить, но AutoMapper - абсолютно правильный ответ. - person RB.; 02.07.2013
comment
Замечательно, что AutoMapper позволяет обойти подобные проблемы, используя ForMember для применения правил сопоставления для конкретных свойств. Это точно отвечает на мой вопрос - спасибо :) - person RB.; 02.07.2013

Resharper позволяет создавать «делегирующих членов», которые копируют интерфейс содержащегося объекта на содержащий объект и туннелируют вызовы методов / доступ к свойствам к содержащемуся объекту.

http://www.jetbrains.com/resharper/webhelp/Code_Generation__Delegating_Members.html

Как только вы это сделаете, вы можете извлечь интерфейс в свой прокси-класс.

person spender    schedule 02.07.2013
comment
Я только что загрузил бесплатную пробную версию, и это, безусловно, сработает для меня :) Как ни странно, он не создает их с новым ключевым словом, что является позором, поэтому мне все равно придется отредактировать его вручную (но, очевидно, намного быстрее !!). Я думаю, что в любом случае сделаю это с помощью T4, так как будет легче создавать новые версии, если модели Contoso изменятся, но большое спасибо за предложение. - person RB.; 02.07.2013