能否将对象图转换为代码?

3
我有一个对象图,设置如下:
Clients string Name List[Address] Addresses
我想把它转换成:
MyClients: Clients string Name List[MyAddress] Addresses
MyAddress: Address String City
我知道可以通过遍历整个对象图来进行类型转换,在给定的示例中,这并不太糟糕。但如果您有一个大的对象图,它会快速变得复杂,并且其次,遍历整个对象图只会产生错误。
我正在寻找以下解决方案:
  • 不修改Clients或Address对象
  • 不遍历对象图
  • 可以用非常少的代码更改实现。

  • 抱歉 - 但这听起来对我来说不太对。你为什么想要进行强制转换呢? - Tobias Langner
    那其实是一个很好的问题。 - Konrad Rudolph
    转换的原因是对象图始于应用程序A,对象图被序列化并传递给应用程序B或应用程序C。应用程序B和C为其自身目的扩展对象图。应用程序A不知道应用程序B或C,也不知道应用程序B和C对对象图正在做什么。应用程序B和C是非常不同的应用程序。 - John Soer
    8个回答

    3
    抱歉,就我所知,在我了解的语言中,无法进行此类转换而不遍历图形。
    对于忽略此要求的简单解决方案,请考虑使用Automapper(假设您使用.NET;如果不是,请指定您开发的平台)。

    1

    你可以为你的图形创建一个包装类,需要时遍历对象图(可能带有一些新创建对象的缓存)。

    请注意,这不是类型转换,而只是在需要时创建一个新的类实例,就像这样:

    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实例将在访问时创建(延迟初始化)。

    [编辑]

    实际上,由于您的新图表应该具有不同的对象(不是从Client派生),所以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);
         }
    }
    

    我喜欢这个解决方案背后的想法。只是为了确认,IEnumerable<Client> GetClients() 应该改为 IEnumerable<MyClient> GetClients(),对吗? - John Soer
    嗯...在我写这段代码时,我假设MyClient是从Client派生的(这样IGraph接口就可以透明地实现)。但你可能不需要这样(我只是举个例子)。在那种情况下,是的,它将是IEnumerable<MyClient>,但MyGraph和InitialGraph将不共享相同的接口。 - vgru

    1
    在您原始问题的评论中,您说:
    “进行强制转换的原因是对象图始于应用程序A,对象图被序列化并传递到应用程序B或应用程序C。应用程序B和C扩展了对象图以满足自己的目的。应用程序A不知道应用程序B或C或应用程序B和C对对象图所做的任何操作。应用程序B和C是非常不同的应用程序。”
    然而,在问题中,您提出了一个好的解决方案:
    “不修改客户端或地址对象”
    强制转换无法为对象添加功能。对象必须已经具备这些功能。
    因此,听起来您不想更改类型,只想在解决方案的某些区域内为这些类型添加额外的设施。
    因此,您需要扩展方法
    您将保留ClientAddress类型,但通过扩展方法添加其他设施:
    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();
    

    我一直在思考这个问题,但仍然存在一个问题。在扩展示例中,MyClients对象仍然具有地址列表,而不是MyAddresses列表。 - John Soer
    听起来你可能误解了一些东西 - 没有 MyClients 类。我写的唯一类别是 static 类,因为你必须将扩展方法放在静态类中(名称无关紧要)。请注意第一个参数前面的 this 关键字;这告诉编译器将该方法“附加”到与该第一个参数相同类型的对象上。所有对象的类型(无论是客户还是地址)都没有改变,但它们获得了额外的功能。因此,没有必要更改任何内容的类型。 - Daniel Earwicker
    你是否清楚.NET中对象类型的工作原理?创建后,无法更改对象的类型。对于值类型(如intbooldoublebyte等),强制转换会创建原始对象的副本。副本是新类型的,但原始对象保持不变。对于引用类型,没有复制。强制转换会创建目标类型的新引用。该引用指向相同的原始对象,因此如果新引用的类型尚未被原始对象支持,则强制转换将失败(抛出异常)。因此,强制转换永远不会更改对象的类型。 - Daniel Earwicker

    1

    序列化和XSLT可能有所帮助,但这将涉及遍历对象图(可能多次)。


    我目前的解决方案是沿着这条线进行操作。应用程序A的对象被序列化并放置在管道上。当应用程序B从管道中拉取数据并对其进行反序列化时,我会进行快速搜索和替换,将Clients替换为MyClients,Address替换为MyAddress。然而,这种方法对我来说似乎非常不专业,我希望能有更好的解决方案。 - John Soer

    0
    var myclients = clients.OfType(typeof(MyClients))
    

    稍微用一下 LINQ 就救了它 :-)


    0
    根据你的要点,答案是:不行。
    为什么不在制作图表时直接创建正确的对象呢?

    0

    你不能在使用对象时进行强制转换吗?


    0

    将更加“通用”的对象转换为更加“具体”的对象不能直接进行。

    你不能将Client强制转换为MyClient,但你可以将MyClient强制转换为Client


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接