Я видел пару подобных потоков к этому вопросу, но ни один из них действительно не отвечает на вопрос, который я хочу задать.
Для начала, к сожалению, я работаю с существующим кодом API так к сожалению, в то время как может быть лучший способ сделать то, о чем я спрашиваю, я привязываюсь к выполнению его так же к способу, которым это - потому что назад совместимость является необоротной.
У меня есть класс ответа, который в настоящее время содержит перечисление для кода ошибки и строкового описания. Коды ошибок определяют довольно хороший и полный набор ответов, которые все очень семантически связаны с операциями, где они используются.
Unfortuantely, я теперь должен добавить другой рабочий процесс для подобного набора объектов API, и это потребует строкового описания, которое прекрасно, но также и перечислимый код ошибки, состоящий из полностью несвязанного набора кодов ошибок. Коды ошибок (и другие аспекты объектной модели) будут использоваться в большом количестве тех же классов, таким образом, было бы хорошо получить движение интерфейса так, чтобы я мог выполнить объекты через ту же платформу.
Намерение здесь состоит в том, чтобы сделать контракт, в котором говорится, что "У меня есть код ошибки и описание того кода ошибки".
Однако насколько я знаю, что нет никакого способа добавить объект к интерфейсу такой как
public interface IError
{
enum ErrorCode;
string Description;
}
и при этом нет способа выразить
public interface IError<T> where T: enum
{
T ErrorCode;
string Description;
}
Кто-либо каждый столкнутый что-то вроде этого прежде?
Да, я сталкивался с этим. Не в этой конкретной ситуации, а в других вопросах о переполнении стека, , таких как этот . (Я не голосую за то, чтобы закрыть этот как дубликат, поскольку он немного отличается.)
Это возможно выразить ваш общий интерфейс - только не на C #. Вы можете сделать это в IL без проблем. Я надеюсь, что это ограничение может быть снято в C # 5. Насколько я видел, компилятор C # действительно правильно обрабатывает ограничение.
Если вы действительно хотите использовать это как вариант, вы можете использовать код, аналогичный тому, что был в Unconstrained Melody , библиотеке, которая у меня есть, которая предоставляет различные методы с этим труднодостижимым ограничением. . Он эффективно использует переписывание IL - он грубый, но он работает для единой системы обмена сообщениями и, вероятно, сработает и для вас. Вы, вероятно, захотите поместить интерфейс в отдельную сборку, что было бы несколько неудобно.
Конечно, вы могли бы вместо этого сделать свой интерфейс просто T: struct
... это было бы не идеально, но, по крайней мере, несколько ограничивало бы тип . Пока вы можете удостовериться, что им не злоупотребляют, это будет работать достаточно хорошо.
Как упомянул Джон Скит, базовый IL поддерживает ограничение обобщенных типов перечислениями, однако C # не позволяет вам воспользоваться этим.
Однако F # допускает такого рода ограничения. Кроме того, если интерфейс определен в F #, ограничение будет применено в коде C #, реализующем интерфейс.Если вы хотите смешивать языки в своем решении, что-то вроде этого должно работать нормально:
type IError<'T when 'T :> System.Enum and 'T : struct> =
abstract member Error : 'T
abstract member Description : string
Если вы поместите это в проект F # и сделаете ссылку на него из своего проекта C #, ваш код C #, реализующий интерфейс, вызовет C # ошибка компилятора при любой попытке использовать его с типом, отличным от перечисления.
Неспособность написать публичный интерфейс IError
- это то, на что мы все жаловались годами. Пока в этом направлении нет прогресса.
Обычно я пишу public interface IError
и оставляю примечание для разработчика, что T должно быть перечислением.
Если я понимаю, что вы хотите сделать, то да, нет способа определить интерфейс, который содержит в качестве одного из членов неспецифическое перечисление. Ваш второй пример близок, но вы ограничены ограничением типа T
структурой
, которая допускает любой тип значения. На этом этапе вам просто нужно полагаться на знание правильного использования интерфейсов, чтобы люди знали, что ожидаемый тип T
должен быть перечислением. Вы могли бы сделать это немного яснее, определив T
как TEnum
или TErrorValue
:
public interface IError<TEnum> where T: struct
{
T ErrorCode;
string Description;
}