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

问题描述 投票:6回答:2

我基本上有以下类(在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所以当只知道接口时,可以使用对象?

c# generics implicit-conversion
2个回答
15
投票

简短回答:

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

答案很长:

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

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

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

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

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

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

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

我怎么能重构MyClass所以当只知道接口时,可以使用对象?

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


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>();
© www.soinside.com 2019 - 2024. All rights reserved.