泛型的隐式操作符不适用于接口

本文关键字:不适用 适用于 接口 操作符 泛型 | 更新日期: 2023-09-27 18:08:19

我基本上有以下类(在c#中发现的为泛型类创建隐式转换的例子?)

class MyClass<T>
{
  public MyClass(T val)
  {
     Value = val;
  }
  public T Value { get; set; }
  public static implicit operator MyClass<T>(T someValue)
  {
     return new MyClass<T>(someValue);
  }
  public static implicit operator T(MyClass<T> myClassInstance)
  {
     return myClassInstance.Value;
  }
}

可以做

MyClass<IFoo> foo1 = new Foo();
MyClass<Foo>  foo2 = new Foo();
//But not
MyClass<IFoo> foo3 = (IFoo)new Foo();

真正的问题发生在尝试执行

之类的操作时。
void Bar(IFoo foo)
{
    Bar2(foo);
    //What should be the same as
    Bar2<IFoo>(new MyClass<IFoo>(foo));
}
void Bar2<T>(MyClass<T> myClass)
{
     //Do stuff
}

我如何重构MyClass,以便在只知道接口的情况下可以使用对象?

泛型的隐式操作符不适用于接口

简短回答:

用户定义的隐式转换在接口上不起作用。不要试图让它工作。找到另一个解决你的类型系统问题的方法。

长答案:

这是c#设计团队深思熟虑的决定。原则是,当您进行涉及接口的转换时,您希望保留引用身份;您正在询问实现接口的对象的标识,而不是试图创建具有类似属性的类似对象。

这里更大的原则是用户定义的转换不应该取代内置的转换。但是,由于几乎任何类都可以被子类化,并且子类可以实现几乎任何接口,因此很难静态地知道给定的用户定义转换是否涉及接口,可能会取代内置转换。

仅供参考,这是规范中特别棘手的一点,c#编译器在这里有一些错误。我怀疑你上面的一个案例利用了这些漏洞;现实世界中有这样做的程序,这使我无法修复这些错误。

这些bug主要是由于这个特性在泛型之前被设计,而在泛型引入了许多不可预见的复杂性之后没有被充分地重新设计。

有关详细信息,请参阅我在这里的大量注释,特别是标记为故意SPEC违章的位,它描述了接口转换的问题。

https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs

如您所见,该文件长度不到一千行,注释可能超过一半。我们花了几周的时间仔细研究,并与语言团队进行了多次讨论,才把这些语义整理出来。一旦你在编译器中犯了一个错误,你经常需要在十年后彻底理解它,然后永远铭记它,以免在升级时破坏客户。对于语言设计者来说,有很多关于c#是如何搞砸规范中这一模糊部分的对象教训。

我如何重构MyClass,以便在只知道接口的情况下可以使用对象?

不要。将接口引用强制转换为实际运行时类型,然后从那里使用它。或者显式地创建所需类型的实例,而不是通过隐式转换。不要尝试玩带有隐式转换和界面的游戏;这不会成功的。

使用'dynamic'关键字进行分配。你可以以后再区分。

  var hook = Environment.Version < new Version(4, 0) ? (dynamic)
    // .NET 2.0->3.5        
    new JITHook<MscorjitAddrProvider>() :
    // .NET 4.0+
    new JITHook<ClrjitAddrProvider>();