13

我已经开始为 FedEx 的网络服务 API 编写接口。他们有 3 个我感兴趣的不同 API;费率、运送和跟踪。我正在使用 SvcUtil.exe 生成服务代理。

不同的服务端点均由 FedEx 在其自己的 WSDL 文件中指定。每个服务端点都有自己的 xml 命名空间(例如http://fedex.com/ws/rate/v5http://fedex.com/ws/ship/v5

服务端点确实使用了很多相同的类型,例如 Address、Measures、Weight、AuthenticationDetail、ClientDetail 等...

这就是问题所在,我可以同时向 SvcUtil.exe 提供所有 WSDL 文件,通常它会将任何相同的类型合并为一个共享类型,但是由于 FedEx 的每个服务都在它们自己的命名空间中,并且他们在该命名空间下的每个 WSDL 文件中重新声明这些类型,而我最终得到的是每个命名空间的地址、地址 1 和地址 2。

为了解决这个问题,我现在要做的是通过 svcutil 分别运行每个 WSDL,并将它们分别放在自己的 .NET 命名空间中(例如 FedEx.Rate、FedEx.Ship、FedEx.Track)。问题在于,现在我在每个命名空间(Fedex.Rate.Address、FedEx.Ship.Address)中都有一个不同的地址类型。

这使得很难概括服务之间使用的代码,例如 GetAuthenticationDetail() 工厂方法,因此我不必在使用不同服务的每个地方重复该代码。

C# 中有什么方法可以将 FedEx.Rate.Address 强制为 FedEx.Ship.Address?

4

4 回答 4

9

如果类型相同,并且您可以控制源类,则可以在类中定义转换运算符,并且任何采用 a 的函数也Rate.Address将自动采用 a Ship.Address。例如:

namespace Rate {
    class Address {
        string Street;
        string City;
        // ...

        public static implicit operator Ship.Address(Rate.Address addr) {
            Ship.Address ret;
            ret.Street = addr.Street;
            ret.City = addr.City;
            // ...

            return ret;
        }
    }
}

我的 C# 有点生疏,但我希望你能明白。

于 2009-02-18T19:55:27.887 回答
7

所以这就是我如何使用反射实现隐式转换运算符。SvcUtil 创建部分类,因此我为每个转换方向添加了一个隐式转换运算符,因此您可以在客户端代码中键入Type1 = Type2.

在这个片段中,WebAuthenticationCredentials 是 WebAuthenticationDetails 的一个属性,因此在迭代源对象的属性时,如果类型不同(内置),它会检查类型的名称(没有命名空间)并递归调用具有这些属性的复制函数.

internal class ReflectionCopy
{
    public static ToType Copy<ToType>(object from) where ToType : new()
    {
        return (ToType)Copy(typeof(ToType), from);
    }

    public static object Copy(Type totype, object from)
    {
        object to = Activator.CreateInstance(totype);

        PropertyInfo[] tpis = totype.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        PropertyInfo[] fpis = from.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

        // Go through each property on the "to" object
        Array.ForEach(tpis, tpi =>
        {
            // Find a matching property by name on the "from" object
            PropertyInfo fpi = Array.Find(fpis, pi => pi.Name == tpi.Name);
            if (fpi != null)
            {
                // Do the source and destination have identical types (built-ins)?
                if (fpi.PropertyType == tpi.PropertyType)
                {
                    // Transfer the value
                    tpi.SetValue(to, fpi.GetValue(from, null), null);
                }
                else
                {
                    // If type names are the same (ignoring namespace) copy them recursively
                    if (fpi.PropertyType.Name == tpi.PropertyType.Name)
                        tpi.SetValue(to, Copy(fpi.PropertyType, tpi.GetValue(from, null)), null);
                }
            }
        });

        return to;
    }
}

namespace Rate
{
    partial class WebAuthenticationDetail
    {
        public static implicit operator Ship.WebAuthenticationDetail(WebAuthenticationDetail from)
        {
            return ReflectionCopy.Copy<Ship.WebAuthenticationDetail>(from);
        }
    }

    partial class WebAuthenticationCredential
    {
        public static implicit operator Ship.WebAuthenticationCredential(WebAuthenticationCredential from)
        {
            return ReflectionCopy.Copy<Ship.WebAuthenticationCredential>(from);
        }
    }
}

namespace Ship
{
    partial class WebAuthenticationDetail
    {
        public static implicit operator Rate.WebAuthenticationDetail(WebAuthenticationDetail from)
        {
            return ReflectionCopy.Copy<Rate.WebAuthenticationDetail>(from);
        }
    }

    partial class WebAuthenticationCredential
    {
        public static implicit operator Rate.WebAuthenticationCredential(WebAuthenticationCredential from)
        {
            return ReflectionCopy.Copy<Rate.WebAuthenticationCredential>(from);
        }
    }
}
于 2009-02-19T01:27:59.727 回答
1

您可以通过创建自己的 Address 实现来使用运算符重载,也可以使用其中一种稳定类型作为属性

一个例子:下面的 Address1 和 Address2 将分别是您的 Rate.Address 和 Ship.Address

class Address1
{
    public string name = "Address1";
}
class Address2
{
    public string name = "Address2";
}

class GenericAddress
{
    public string name = "GenericAddress";
    public static implicit operator GenericAddress(Address1 a)
    {
        GenericAddress p = new GenericAddress(); p.name = a.name; return p;
    }
    public static implicit operator GenericAddress(Address2 a)
    {
        GenericAddress p = new GenericAddress(); p.name = a.name; return p;
    }
}
class Program
{
    static void Main(string[] args)
    {
        PrintName(new Address1());//prints address1
        PrintName(new Address2());//prints address2
    }

    static void PrintName(GenericAddress a)
    {
        Console.WriteLine(a.name);
    }
}

编辑:方法与上面的帖子相同,实现是在一个单独的类中,仅此而已

于 2009-02-18T20:07:38.547 回答
1

那些生成的类是否定义为“部分”?如果是这样,您可以在不同的文件中扩展它们并提取一个接口并让它由所有地址类实现。

于 2009-02-18T20:52:03.743 回答