Принуждение типов в разных пространствах имен с одинаковым расположением в C # - PullRequest
12 голосов
/ 18 февраля 2009

Я начал писать интерфейс для API веб-сервиса FedEx. У них есть 3 разных API, которые меня интересуют; Оценить, отправить и отслеживать. Я создаю сервисные прокси с помощью SvcUtil.exe.

Различные конечные точки службы указываются FedEx в своих собственных файлах WSDL. Каждая конечная точка службы имеет свое собственное пространство имен xml (например, http://fedex.com/ws/rate/v5 и http://fedex.com/ws/ship/v5)

Конечные точки службы используют довольно много идентичных типов, таких как адрес, измерения, вес, AuthenticationDetail, ClientDetail и т. Д ...

И вот в чем проблема, я могу одновременно предоставить все файлы WSDL в SvcUtil.exe, и обычно он объединяет любые идентичные типы в один общий тип, но так как каждая из служб FedEx находится в своих собственных пространства имен, и они повторно объявляют эти типы в каждом файле WSDL в этом пространстве имен, и вместо этого я получаю в итоге адреса, адреса1 и адреса2, по одному для каждого пространства имен.

Чтобы решить эту проблему, я теперь запускаю каждый WSDL через svcutil отдельно и помещаю их в свое собственное пространство имен .NET (например, FedEx.Rate, FedEx.Ship, FedEx.Track). Проблема в том, что теперь у меня есть отдельный тип адреса в каждом пространстве имен (Fedex.Rate.Address, FedEx.Ship.Address).

Это затрудняет обобщение кода, используемого между службами, например, фабричный метод GetAuthenticationDetail (), поэтому мне не нужно повторять этот код в каждом месте, где я использую различные службы.

Есть ли какой-нибудь способ в C # принудить FedEx.Rate.Address к FedEx.Ship.Address?

Ответы [ 4 ]

8 голосов
/ 18 февраля 2009

Если типы идентичны, и вы имеете контроль над исходными классами, вы можете определить оператор преобразования в классе, и любая функция, которая принимает Rate.Address, также автоматически примет 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 # немного ржавый, но я надеюсь, что вы поняли.

7 голосов
/ 19 февраля 2009

Итак, вот как я реализовал операторы неявного преобразования, используя отражение. 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);
        }
    }
}
1 голос
/ 18 февраля 2009

Определены ли эти сгенерированные классы как "частичные"? Если это так, вы можете расширить их в другом файле, извлечь интерфейс и позволить его реализовать всем классам Address.

1 голос
/ 18 февраля 2009

вы можете использовать перегрузку операторов, создав собственную реализацию 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);
    }
}

Edit: подход такой же, как и в посте выше, реализация в отдельном классе, который все

...