Принуждение типов в разных пространствах имен с одинаковым расположением в C#
Я начал писать интерфейс для 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 ответа
Если типы идентичны, и у вас есть контроль над исходными классами, вы можете определить оператор преобразования в классе и любую функцию, которая принимает 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# немного ржавый, но я надеюсь, вы поняли идею.
Итак, вот как я реализовал операторы неявного преобразования, используя отражение. 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);
}
}
}
Эти сгенерированные классы определены как "частичные"? Если это так, вы можете расширить их в другом файле и извлечь интерфейс и позволить его реализовать всем классам Address.
Вы можете использовать перегрузку операторов, создав собственную реализацию 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);
}
}
Изменить: подход такой же, как в посте выше, реализация находится в отдельном классе, который все