繁体   English   中英

实现通用接口时的奇怪C#行为

[英]Odd C# behavior when implementing generic 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类型。 但好吧,我可以接受编译器比我聪明:-)

编辑 :编译器并不比我聪明:-)根据评论为什么这会导致CS0695? 我们有“在确定所有可能的构造类型时不考虑约束声明”。

现在我交换接口声明:

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

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

错误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 实现IHandle<T> 确实有效(如果我已经显示了某些人可能已经看过它的代码)。 这有效:

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 )。 咄!

@Siram指出,唯一性问题(但不是订单方面)已经回答了为什么这会导致CS0695?

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

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

问题解决了 - 我发现了微妙的差异。 当声明的顺序被交换我不能移动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 ..
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM