У меня есть 3 класса, которые по сути одинаковы, но не реализуют интерфейс, потому что все они происходят из разных веб-сервисов.
например
Все они имеют одинаковые свойства, и я пишу код, чтобы сопоставить их друг с другом, используя промежуточный объект, который реализует мой собственный интерфейс IObject1.
Я сделал это с помощью дженериков
public static T[] CreateObject1<T>(IObject1[] properties)
where T : class, new()
{
//Check the type is allowed
CheckObject1Types("CreateObject1<T>(IObject1[])", typeof(T));
return CreateObjectArray<T>(properties);
}
private static void CheckObject1Types(string method, Type type)
{
if (type == typeof(Service1.Object1)
|| type == typeof(Service2.Object1)
|| type == typeof(Service3.Object1)
|| type == typeof(Service1.Object1[])
|| type == typeof(Service2.Object1[])
|| type == typeof(Service3.Object1[]))
{
return;
}
throw new ArgumentException("Incorrect type passed to ServiceObjectFactory::" + method + ". Type:" + type.ToString());
}
Мой клиентский код выглядит так:
//properties is an array of my intermediary objects
Object1[] props = ServiceObjectFactory.CreateObject1<Object1>(properties);
Что я хочу сделать, так это избавиться от метода CheckObject1Types и вместо этого использовать ограничения, чтобы получить ошибку сборки, если типы недействительны, потому что в данный момент я могу вызвать этот метод с любым типом, а исключение ArgumentException выбрасывается CheckObject1Types метод.
Итак, я хотел бы сделать что-то вроде:
public static T[] CreateObject1<T>(IObject1[] properties)
where T : class, new(), Service1.Object1|Service2.Object1|Service3.Object1
{
return CreateObjectArray<T>(properties);
}
Есть идеи?
Редактировать: Я не хочу изменять файлы Reference.cs для каждого веб-сервиса, потому что все, что требуется, - это товарищ по команде, чтобы обновить веб-ссылку и БАМ! сломанный код.





Ограничение списка классов способом «ИЛИ», как вы хотите, невозможно в C#. (На самом деле, я даже не уверен, что это разрешено непосредственно в IL.)
Единственный вариант - продолжать использовать функции стиля checktypes. Если вы владеете кодом для различных веб-сервисов, вы также можете реализовать «дозорный» интерфейс и использовать его в качестве ограничения. Я знаю, что сигнальные интерфейсы не рекомендуются в соответствии с Руководством по разработке фреймворка, но иногда они находят свое применение (это одно из них).
Как указывает Джон, вы можете использовать классы prtial для реализации общего интерфейса. Если ваш References.cs реализует класс:
namespace TestServices
{
internal partial class Service1SoapClient : System.ServiceModel.ClientBase<T>, K
{
}
}
Затем вы должны создать еще один частичный класс в том же пространстве имен (назовите его References.CommonInterface.cs), который может находиться в любом месте вашего проекта, со следующим:
namespace TestServices
{
internal interface ICommon
{
}
internal partial class Service1SoapClient : ICommonInterface
{
}
}
Честно говоря, я надеюсь, что вы оказались неправы! Любое расширение относительно того, почему это так? У меня нет кода - да, я мог бы пройти через Reference.cs и вручную назначить каждый класс общему интерфейсу, но если товарищ по команде обновит веб-ссылки - БАМ! Больше никакого интерфейса.
См. Ответ Джона Скита относительно возможности использования частичных классов для получения всего из общего интерфейса.
См. Эту статью о том, почему не была реализована возможность «включения типа», что, вероятно, имеет много тех же причин. blogs.msdn.com/peterhal/archive/2005/07/05/435760.aspx
Если вы извлекаете эти объекты из веб-службы, у вас, безусловно, есть контроль над используемыми определениями классов. Они не возникают из воздуха (даже если у вас есть генератор кода или визуальная студия, создающая их изначально). Для каждого из них все еще есть файл класса, который должен быть скомпилирован с приложением, и вы должны иметь возможность добавить свой общий интерфейс к этим определениям классов.
Разве не для этого нужен контроль версий?
Правда, я просто не люблю этого делать, наверное. Код запаха?
Даже при использовании системы управления версиями каждый раз, когда вы вносите изменения в reference.cs, они будут повторяться. Кому-то все равно придется пойти и вручную вернуться к "Интерфейсной" копии ...
Предполагая, что сгенерированные классы являются частичными, вы можете создать интерфейс, а затем добавить еще один частичный исходный файл, чтобы ваши сгенерированные классы реализовали интерфейс. Затем вы можете ограничить интерфейс как обычно. Никаких изменений в сгенерированном коде не требуется :)
Это вроде того, что я пытался понять.
Хорошее замечание о трюке с частичным классом. Я только что посмотрел на новый проект, который я создал в VS2008 (.NET 3.5), используя ссылку на службу, и он действительно создал клиента как частичный класс.
Интересно ... Я попробую.
Хорошая идея. Я не знал, что у вас действительно есть доступ к исходному коду сервисного агента. Это было бы идеальным решением вашей проблемы. Проголосуйте от меня
Джон, вы разместили пример этого на своем (фантастическом) веб-сайте? Мне было бы очень интересно увидеть это.
@endian: У меня нет примера на веб-сайте, но я недавно проделал то же самое в Protocol Buffers. Так что см. Пример github.com/jskeet/dotnet-protobufs/tree/master/csharp/… :)
Я бы написал класс конвертера, который преобразовал любой из ваших трех объектов в новый объект, поддерживающий нужный интерфейс. Кроме того, я бы использовал отражение, чтобы вам не приходилось вводить все назначения вручную (если только это не небольшой объект и не ожидается, что он изменится слишком сильно).
Использование Reflection также даст вам гарантию того, что вы хотите убедиться, что объекты реализуют те свойства, которые реализует ваш новый объект Interfaced, в противном случае, когда свойство, которое вы ожидаете, не реализовано, вы можете вызвать ошибку.
Я только что заметил, что могу повысить эффективность кода типов проверок, изменив его с && на || и! = to ==, прежде чем кто-нибудь укажет на это.