具有泛型的隐式运算符不适用于接口

时间:2016-09-21 14:03:20

标签: c# generics implicit-conversion

我基本上有以下课程(C# creating an implicit conversion for generic class?上的例子)。

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,以便只知道接口时才能使用对象?

2 个答案:

答案 0 :(得分:15)

简短回答:

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

答案很长:

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

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

仅供参考,这是规范中特别棘手的一点,C#编译器在这里有一些错误。我怀疑你上面的一个案例利用了这些错误;事实上,有真实世界的程序这样做是阻止我修复错误的原因。

这些错误主要是这个功能在仿制药之前设计的结果,然后在仿制药引入许多不可预见的并发症之后没有充分重新设计。

有关详细信息,请参阅此处的大量注释,特别是标记为DELIBERATE SPEC VIOLATION的位,用于描述界面转换的问题。

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

正如您所看到的,此文件长度不到一千行,可能超过一半注释。经过了的仔细研究,并与语言团队进行了许多讨论,以便对这些语义进行整理。一旦你在编译器中犯了一个错误,你通常必须在十年后彻底地理解它,然后永远地将它包含在内,以免在升级时破坏客户。语言设计者有很多关于C#如何搞砸了规范这个模糊部分的对象课程。

  

如何重构MyClass,以便只知道接口时才能使用对象?

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

答案 1 :(得分:0)

使用“ dynamic”关键字进行分配。您可以稍后进行区分。

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