Можно ли привести граф объектов?

У меня есть граф объектов, который настроен так

клиенты
    Имя строки
    Список [Адрес] Адреса

Я хотел бы бросить это на

MyClients: клиенты
    Имя строки
    Список [MyAddress] Адреса

Мой адрес: Адрес
    Струнный Город

Я знаю, что могу разыграть это, пройдясь по всему графу объектов и в данном примере это было бы неплохо. Но если у вас большой объектный граф, он очень быстро становится волосатым, и, во-вторых, кажется совершенно неправильным обходить весь объектный граф.

Я ищу решение, которое

  • Не изменяет объекты клиентов или адреса
  • Не ходит по графу объектов
  • Может быть реализовано с минимальными изменениями кода.

  • 13.10.2009 07:26:37
    извините - но это звучит неправильно для меня. Почему ты хочешь это разыграть?
    Tobias Langner 13.10.2009 07:32:12
    Это на самом деле хороший вопрос.
    Konrad Rudolph 13.10.2009 07:33:20
    Причина приведения заключается в том, что граф объектов запускается в приложении A, а граф объектов сериализуется и передается приложению B или приложению C. Приложения B и C расширяют граф объектов для своих собственных целей. Приложение A не знает о приложении B или C или о том, что приложения B и C делают с графом объектов. Приложения B и C - это совершенно разные приложения.
    John Soer 13.10.2009 07:46:23
    8 ОТВЕТОВ
    РЕШЕНИЕ

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

    Причина приведения заключается в том, что граф объектов запускается в приложении A, а граф объектов сериализуется и передается приложению B или приложению C. Приложения B и C расширяют граф объектов для своих собственных целей. Приложение A не знает о приложении B или C или о том, что приложения B и C делают с графом объектов. Приложения B и C - это совершенно разные приложения

    И все же в вопросе вы говорите хорошее решение:

    Не изменяет объекты клиентов или адреса

    Приведение не может добавлять возможности к объекту. Объект уже должен иметь эти возможности.

    Похоже, вы не хотите менять типы, вы просто хотите добавить дополнительные возможности к этим типам в определенных областях вашего решения.

    Так что вам нужны методы расширения .

    Вы бы сохранили типы Clientи Address, но добавили дополнительные возможности с помощью методов расширения:

    public static class MyExtensions
    {
        public static void SendLetter(this Address address, string messageBody)
        {
            // blah
        }
    }

    Это позволяет вам написать:

    someClient.Addresses[0].SendLetter("Dear Sir, K THX BAI");

    Возможно, вам придется хранить дополнительные данные вместе с каждым Addressобъектом. Вероятно, наиболее удобное стандартное решение для этого состоит в создании Addressпроизводных от DependencyObject , который позволяет хранить дополнительные данные, используя в качестве ключа экземпляр DependencyProperty :

    public static class MyExtensionsWithData
    {
        // declare one of these for each "data slot" you'll be using
        public static readonly DependencyProperty PhoneProperty = 
            RegisterAttached("PhoneNumber", 
                             typeof(string), 
                             typeof(MyExtensionsWithData));
    
        public static void SetPhoneNumber(this Address address, string phone)
        {
            address.SetValue(PhoneProperty, phone);
        }
    
        public static string GetPhoneNumber(this Address address)
        {
            return (string)address.GetValue(PhoneProperty);
        }
    }

    Таким образом, вы можете эффективно добавлять новые свойства к существующему объекту и использовать их следующим образом:

    // set
    someClient.Addresses[0].SetPhoneNumber("5550945793847");
    
    // get
    string phoneNum = someClient.Addresses[0].GetPhoneNumber();
    1
    13.10.2009 08:49:23
    Я думал об этом, но это все еще имеет проблему. В примере расширения объект MyClients по-прежнему имеет список адресов, а не список MyAddresses
    John Soer 13.10.2009 23:19:04
    Похоже, вы что-то не так поняли - MyClientsкласса нет . Единственные классы, которые я написал, были staticклассы, так как вы должны поместить методы расширения в статический класс (имя которого не имеет значения). Запишите thisключевое слово перед первым параметром; это говорит компилятору «присоединять» метод к объекту того же типа, что и первый параметр. Типы всех объектов (будь то клиенты или адрес) не изменились, но они получили дополнительные возможности. Так что нет необходимости менять тип чего-либо.
    Daniel Earwicker 14.10.2009 07:03:23
    Интересно, вам понятно, как работают типы объектов в .NET? Вы не можете изменить тип объекта после его создания. Для типов значений ( int, bool, double, byteи т.д.), литые создает копию исходного объекта. Копия нового типа, но оригинал не изменился. Для ссылочных типов копирование отсутствует. Приведение создает новую ссылку целевого типа. Ссылка указывает на тот же исходный объект, поэтому приведение завершится неудачно (сгенерирует исключение), если тип новой ссылки еще не поддерживается исходным объектом. Таким образом, приведения никогда не меняют тип объекта.
    Daniel Earwicker 14.10.2009 07:09:52

    Извините, AFAIK, невозможно выполнить такое преобразование без обхода графа на языках, которые я знаю.

    Для простого решения, игнорирующего это требование, рассмотрите возможность использования Automapper (при условии, что вы используете .NET; если нет, укажите платформу, на которой вы разрабатываете)

    3
    13.10.2009 07:31:28
    var myclients = clients.OfType(typeof(MyClients))

    немного LINQ на помощь :-)

    0
    13.10.2009 07:31:33

    Учитывая ваши точечные точки, ответ: Нет.

    Почему бы вам просто не создать правильные объекты при создании графика?

    0
    13.10.2009 07:31:35

    Разве вы не можете просто разыграть их, когда используете объекты?

    0
    13.10.2009 07:32:18

    Преобразование более общего объекта в более конкретный не будет работать напрямую.

    Вы не можете бросить , Clientчтобы , MyClientно вы можете бросить MyClientв Client.

    0
    13.10.2009 07:40:08

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

    Обратите внимание, что это не приведение, но вы просто создаете новый экземпляр вашего класса, когда это необходимо, что-то вроде:

    public class InitialGraph : IGraph
    {
         IEnumerable<Client> GetClients()
         {
             ...
         }
    }

    а затем вы заверните это так:

    public class MyGraph : IGraph
    {
         private readonly IGraph _initial;
         public MyGraph(IGraph initial)
         {
             _initial = initial;
         }
    
         IEnumerable<Client> GetClients()
         {
              foreach (Client c in _initial.GetClients())
                  yield return new MyClient(c);
         }
    }

    Ваши MyClientэкземпляры будут созданы при доступе (ленивый init).

    [Редактировать]

    На самом деле, поскольку ваш новый граф должен иметь разные объекты (не производные от клиента), GetClients()метод не может вернуться IEnumerable<Client>, но:

    // note that this class doesn't implement IGraph anymore
    public class MyGraph
    {
         private readonly IGraph _initial;
         public MyGraph(IGraph initial)
         {
             _initial = initial;
         }
    
         IEnumerable<MyClient> GetClients()
         {
              foreach (Client c in _initial.GetClients())
                  yield return new MyClient(c);
         }
    }
    1
    13.10.2009 08:29:13
    Мне нравится идея этого решения. Просто чтобы убедиться, что IEnumerable <Client> GetClients () не должен быть IEnumerable <MyClient> GetClients ()
    John Soer 13.10.2009 08:10:28
    Хммм ... Когда я писал это, я предполагал, что MyClient является производным от клиента (чтобы интерфейс IGraph был реализован прозрачно). Но вам это может не понадобиться (я просто написал это в качестве примера). В этом случае, да, это будет IEnumerable <MyClient>, но MyGraph и InitialGraph не будут использовать один и тот же интерфейс.
    Groo 13.10.2009 08:27:34

    Сериализация и XSLT могут помочь, но для этого потребуется пройти по графу объектов (возможно, несколько раз).

    1
    13.10.2009 07:52:47
    Мое текущее решение я делаю что-то в этом направлении. Объекты по приложению A сериализуются и помещаются в трубу. Когда приложение B извлекает данные канала и десериализует их, я выполняю быстрый поиск и замену, чтобы заменить клиентов на MyClients и Address на MyAddress. Однако это кажется мне очень хакерским, и я надеялся, что будет лучшее решение.
    John Soer 13.10.2009 08:13:45