Учитывая этот интерфейс «IHandle» и два класса, которые будут обрабатываться:Одд C поведения # при реализации универсального интерфейса
interface IHandle<T>
{
void Handle(T m);
}
class M1
{
public int Id;
}
class MReset
{
}
Я хочу, чтобы создать общую базу, которая заботится о «перезагрузке», а также управлении экземплярами M1 :
class HandlerBase<T> :
IHandle<MReset>,
IHandle<T> where T : M1
{
protected int Count;
void IHandle<T>.Handle(T m)
{
++Count;
Console.WriteLine("{0}: Count = {0}", m.Id, Count);
}
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
}
}
Это не компилируется, поскольку компилятор считает, Т может быть "MReset", поэтому он выводит:
ошибки CS0 695: «HandlerBase» не может реализовать оба «IHandle» и «IHandle», потому что они могут объединиться для какого-либо параметра типа замены
Это само по себе является немного странным, так как я не могу видеть, как T могло быть типа MReset так как он должен быть типа M1. Но хорошо, я могу признать, что компилятор умнее меня :-)
Edit: компилятор не умнее меня :-) Согласно комментарию на Why does this result in CS0695? мы имеем «декларацию Constraint не учитываются при определении все возможные построенные типы ".
Теперь я переставить декларации интерфейса:
class HandlerBase<T> :
IHandle<T> where T : M1,
IHandle<MReset>
{
... same as before ..
}
И вдруг я получаю другое сообщение об ошибке о том, что я не могу реализовать IHandle.Handle (MReset м), так как объявление класса не утверждает, что она осуществляет что интерфейс:
ошибка CS0540: 'HandlerBase.IHandle < ...> Ручка (MReset)': содержащие типа не реализует интерфейс 'IHandle'
Вопрос: почему порядок заявлений имеет такое значение? Что происходит во втором примере?
В конце концов оказывается, что есть решение:
class HandlerBase :
IHandle<MReset>
{
protected int Count;
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
}
}
class Handler<T> : HandlerBase,
IHandle<T> where T : M1
{
void IHandle<T>.Handle(T m)
{
++Count;
Console.WriteLine("{0}: Count = {0}", m.Id, Count);
}
}
Но решение работает только если HandlerBase
орудия IHandle<MReset>
- нет, если общий интерфейс IHandle<T>
реализован в HandlerBase
первой. Почему?
Редактировать: Реализация IHandle<T>
в HandlerBase
делает работу (и если я показал код кто-то видел его). Это работает:
class HandlerBase<T> :
IHandle<T> where T : M1
{
protected int Count;
void IHandle<T>.Handle(T m)
{
++Count;
Console.WriteLine("Type = {0}, Id = {1}, Count = {2}", GetType(), m.Id, Count);
}
}
class Handler<T> : HandlerBase<T>,
IHandle<MReset>
where T : M1
{
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
Console.WriteLine("RESET");
}
}
К сожалению, моя вторая декларация класса была такова:
class Handler<T> : HandlerBase<T> where T : M1,
IHandle<MReset>
{
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
Console.WriteLine("RESET");
}
}
Обратите внимание на тонкое различие в расположении where T : M1
:-) Последний пример заявляет, что T должен реализовать IHandle<MReset>
(в дополнение к M1
). Duh!
[Родственные/дубликаты] (http://stackoverflow.com/questions/15316898/why-does-this-result-in-cs0695). Я не закрываю как дубликат только из-за этого вопроса: «Почему порядок объявлений делает такую разницу?» на который там не ответил. –
Это действительно релевантная ссылка. Я только что отредактировал принятый ответ, чтобы сделать важную часть информации из спецификации C# более заметной: ** «Объявления ограничений не учитываются при определении всех возможных построенных типов». ** –