【问题标题】:implicit operator with generic not working for interface具有泛型的隐式运算符不适用于接口
【发布时间】:2016-09-21 14:03:19
【问题描述】:

我基本上有以下课程(示例见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 以便在只知道接口的情况下使用对象?

【问题讨论】:

  • 我不明白你真正想用这个实现什么。 MyClass 总是使用 IFoo 对象(接口和实现)还是可以使用其他任何东西?无论如何,如果你试图隐式转换接口,在 C# 中是不可能的(正如@EricLippert 已经说过的)
  • 会有多个 IFoo 实现(Foo1、Foo2、.. FooN)。可能还有其他接口 Bar(OtherInterface other) { Bar2(other); } 当然,这个问题是实际问题的精简版。

标签: c# generics implicit-conversion


【解决方案1】:

简答:

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

长答案:

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

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

仅供参考,这是规范中特别棘手的部分,C# 编译器在这里有一些错误。我怀疑您的上述案例之一利用了这些错误;现实世界的程序会这样做,这一事实使我无法修复这些错误。

这些错误主要是由于在泛型之前设计了此功能,然后在泛型引入了许多无法预料的复杂情况后没有充分重新设计。

有关详细信息,请参阅我的大量 cmets,尤其是标记为 DELIBERATE SPEC VIOLATION 的位,这些位描述了接口转换的问题。

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

如您所见,这个文件不到一千行,可能超过一半 cmets。我们花了 几周 的仔细研究和与语言团队的多次讨论来整理这些语义。一旦你在编译器中犯了一个错误,你通常必须在十年后彻底理解它,然后永远铭记它,以免在升级时破坏客户。语言设计者有很多关于 C# 如何弄乱规范中这个晦涩的部分的对象教训。

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

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

【讨论】:

  • 有 Foo1, Foo2, ..., FooN。关键是 MyClass 不应该关心,因为 Bar() 不会知道。 (未来可能会有 FooX)。因此,不能选择转换为实际的运行时类型。
  • 确实如此。如果我写 'void Bar(MyClass foo)' 我可以注入任何 IFoo 实现(Foo1、Foo2、..、FooN、FooX),因为此时隐式转换将是有效的......
  • 这种情况怎么样(特别是为了统一) Map(它只包含一个 T[,]),我希望能够写public static implicit operator Texture2D(Map&lt;Color&gt; map)public static implicit operator Texture2D(Map&lt;float&gt; map)public static implicit operator Texture2D(Map&lt;byte&gt; map)方法,但我收到 CS0556 错误,有没有办法解决这个问题?
【解决方案2】:

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

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

【讨论】:

  • 已确认。对我来说,做(MyClass)(otherClassObject as dynamic),其中otherClassObject 是一个接口类型触发了MyClass 上定义的隐式转换。如果没有as dynamic,则不会触发强制转换。
猜你喜欢
  • 2015-12-07
  • 1970-01-01
  • 2012-04-25
  • 2010-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多