实现通用接口

时间:2015-04-24 09:26:35

标签: c# generics inheritance interface

给定这个“IHandle”接口和两个要处理的类:

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;
  }
}

这不编译,因为编译器认为T可以是“MReset”,因此它输出:

  

错误CS0695:'HandlerBase'无法同时实现'IHandle'   和'IHandle',因为他们可能统一某些类型参数   取代

这本身就有些奇怪,因为我无法看到T可能是MReset类型,因为它必须是M1类型。但好吧,我可以接受编译器比我聪明: - )

编辑:编译器并不比我聪明:-)根据对Why does this result in CS0695?的评论,我们有“在确定所有可能的构造类型时不考虑约束声明”。

现在我交换了接口声明:

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

突然间我收到一条不同的错误消息,指出我无法实现IHandle.Handle(MReset m),因为类声明没有声明它正在实现该接口:

  

错误CS0540:'HandlerBase.IHandle&lt; ...&gt; .Handle(MReset)':含有   type没有实现接口'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之外)。杜!

2 个答案:

答案 0 :(得分:1)

@Siram指出Why does this result in CS0695?已经回答了唯一性问题(但不是订单方面):

C#语言规范(https://www.microsoft.com/en-us/download/confirmation.aspx?id=7029)讨论了实现的接口的唯一性&#34;在13.4.2中:&#34;通用类型声明实现的接口必须对所有可能的构造类型保持唯一。&#34;稍后,在描述检查的详细信息时:&#34;在确定所有可能的构造类型时,不会将约束声明视为。&#34;

为什么会这样,我不确定;或许可以构造嵌套或链式约束,这使得编译器无法证明唯一性,或者不能通过程序集传递所有约束(我认为这对于通用语言规则是必要的)。

答案 1 :(得分:1)

问题解决了 - 我发现了微妙的差异。当交换声明的顺序时,我应该移动where T : M1,因为IHandle<MReset>约束最终会应用于T而不是类声明:

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 ..
}