泛型的隐式操作符不适用于接口
本文关键字:不适用 适用于 接口 操作符 泛型 | 更新日期: 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>();