2015-04-24 3 views
7

Учитывая этот интерфейс «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!

+0

[Родственные/дубликаты] (http://stackoverflow.com/questions/15316898/why-does-this-result-in-cs0695). Я не закрываю как дубликат только из-за этого вопроса: «Почему порядок объявлений делает такую ​​разницу?» на который там не ответил. –

+0

Это действительно релевантная ссылка. Я только что отредактировал принятый ответ, чтобы сделать важную часть информации из спецификации C# более заметной: ** «Объявления ограничений не учитываются при определении всех возможных построенных типов». ** –

ответ

1

Проблема решена - я нашел тонкую разницу. Когда порядок деклараций поменялись местами, я должен не ход where T : M1 поскольку IHandle<MReset> ограничение, то в конечном итоге применяется к Т вместо объявления класса:

class HandlerBase<T> : 
    IHandle<T> where T : M1, 
    IHandle<MReset> 
{ 
    ... same as before .. 
} 

Правильное повторное упорядочение должно быть:

class HandlerBase<T> : 
    IHandle<T>, 
    IHandle<MReset> 
    where T : M1 
{ 
    ... same as before .. 
} 
1

@Siram отметил, что проблема единственности (но не аспект порядка) был дан ответ в Why does this result in CS0695?:

C# язык спецификации (https://www.microsoft.com/en-us/download/confirmation.aspx?id=7029) обсуждает «Уникальность реализованных интерфейсов» в 13.4.2 .: «Интерфейсы, реализованные общим объявлением типа, должны оставаться уникальными для всех возможных построенных типов». И позже, при описании деталей проверки: «При определении всех возможных сконструированных типов декларации ограничений не рассматриваются».

Почему это так, я не уверен; возможно, можно построить вложенные или цепочки ограничения, которые делают невозможным для компилятора доказать уникальность, или не все ограничения могут быть переданы через сборки (что, я думаю, было бы необходимо для общего правила языка).

+0

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

+0

По-видимому, компилятор * делает * рассматривать объявления ограничений неожиданным образом. Если я удалю 'where T: M1', порядок объявлений станет несущественным, и обе последовательности приводят к CS0695« ... потому что они могут унифицироваться для некоторых подстановок параметров типа »- как и следовало ожидать. –

Смежные вопросы