C# Generics: могу ли я ограничиться набором классов, которые не реализуют интерфейс?

У меня есть 3 класса, которые по сути одинаковы, но не реализуют интерфейс, потому что все они происходят из разных веб-сервисов.

например

  • Service1.Object1
  • Service2.Object1
  • Service3.Object1

Все они имеют одинаковые свойства, и я пишу код, чтобы сопоставить их друг с другом, используя промежуточный объект, который реализует мой собственный интерфейс 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 для каждого веб-сервиса, потому что все, что требуется, - это товарищ по команде, чтобы обновить веб-ссылку и БАМ! сломанный код.

Я только что заметил, что могу повысить эффективность кода типов проверок, изменив его с && на || и! = to ==, прежде чем кто-нибудь укажет на это.

Rob Stevenson-Leggett 20.10.2008 19:52
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
1
617
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ограничение списка классов способом «ИЛИ», как вы хотите, невозможно в 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 и вручную назначить каждый класс общему интерфейсу, но если товарищ по команде обновит веб-ссылки - БАМ! Больше никакого интерфейса.

Rob Stevenson-Leggett 20.10.2008 19:59

См. Ответ Джона Скита относительно возможности использования частичных классов для получения всего из общего интерфейса.

Scott Dorman 20.10.2008 20:23

См. Эту статью о том, почему не была реализована возможность «включения типа», что, вероятно, имеет много тех же причин. blogs.msdn.com/peterhal/archive/2005/07/05/435760.aspx

Scott Dorman 20.10.2008 20:25

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

Разве не для этого нужен контроль версий?

Joel Coehoorn 20.10.2008 20:04

Правда, я просто не люблю этого делать, наверное. Код запаха?

Rob Stevenson-Leggett 20.10.2008 20:20

Даже при использовании системы управления версиями каждый раз, когда вы вносите изменения в reference.cs, они будут повторяться. Кому-то все равно придется пойти и вручную вернуться к "Интерфейсной" копии ...

Ricardo Villamil 21.10.2008 00:30
Ответ принят как подходящий

Предполагая, что сгенерированные классы являются частичными, вы можете создать интерфейс, а затем добавить еще один частичный исходный файл, чтобы ваши сгенерированные классы реализовали интерфейс. Затем вы можете ограничить интерфейс как обычно. Никаких изменений в сгенерированном коде не требуется :)

Это вроде того, что я пытался понять.

Joel Coehoorn 20.10.2008 20:18

Хорошее замечание о трюке с частичным классом. Я только что посмотрел на новый проект, который я создал в VS2008 (.NET 3.5), используя ссылку на службу, и он действительно создал клиента как частичный класс.

Scott Dorman 20.10.2008 20:18

Интересно ... Я попробую.

Rob Stevenson-Leggett 20.10.2008 20:21

Хорошая идея. Я не знал, что у вас действительно есть доступ к исходному коду сервисного агента. Это было бы идеальным решением вашей проблемы. Проголосуйте от меня

Kilhoffer 20.10.2008 20:25

Джон, вы разместили пример этого на своем (фантастическом) веб-сайте? Мне было бы очень интересно увидеть это.

endian 21.10.2008 00:37

@endian: У меня нет примера на веб-сайте, но я недавно проделал то же самое в Protocol Buffers. Так что см. Пример github.com/jskeet/dotnet-protobufs/tree/master/csharp/… :)

Jon Skeet 21.10.2008 01:35

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

Использование Reflection также даст вам гарантию того, что вы хотите убедиться, что объекты реализуют те свойства, которые реализует ваш новый объект Interfaced, в противном случае, когда свойство, которое вы ожидаете, не реализовано, вы можете вызвать ошибку.

Другие вопросы по теме