你能用隐式转换来满足一般约束吗?

本文关键字:约束 满足 转换 | 更新日期: 2023-09-27 18:14:54

给定这些类型:

class A { }
class B
{
    public static implicit operator A(B me)
    {
        return new A();
    }
}
class Test<T> where T : A { }

我试着

var b = new Test<B>();

并预期它会失败,它确实失败了。但是错误信息是

类型'B'不能用作泛型类型或方法'Test'中的类型参数'T'。没有从'B'到'A'的隐式引用转换。

但是从B到a的隐式引用转换,这只是一个奇怪的消息吗?是隐式引用转换,正如Adam Robinson的回答所示。消息是正确的。

注意MSDN说:

where T:(基类名)-类型参数必须是或派生自指定的基类。

这就解释了为什么不允许,因为B不派生自A

你能用隐式转换来满足一般约束吗?

不,你想做的是不可能的。隐式引用转换与隐式类型转换不同。您的代码定义了隐式类型转换,您可以在其中执行以下操作:

B foo = new B();
A bar = foo;

注意,foobar现在包含不同的引用。隐式类型转换创建A实例,该实例应该(按照约定)在逻辑上等同于foo。但关键是它是一个不同的引用。

引用转换是在引用本身没有改变的地方,这意味着所讨论的类型必须要么继承(对于类),要么实现(对于接口)所讨论的类型。如果我这样做:

class A { }
class B : A { }

那么我上面的代码现在将在foobar中保持相同的引用。这就是隐式引用转换的含义。相反,显式引用转换将是向下转换的,如下所示:

A foo = new B();
B bar = (B)foo;

同样,引用是相同的,但强制类型转换是显式的。

所以,简而言之,MSDN文档更清晰,但不那么精确。

这是不可能的。

隐式转换不同于类型等价。仅仅因为一种类型可以转换为另一种类型并不意味着它是第二种类型的特定形式。因此,它不适用于泛型约束。

这是很有意义的——想想下面编译器会做什么:

class A 
{
    public void Foo();
}
class B
{
    public static implicit operator A(B me)
    {
        return new A();
    }
}

现在,假设你有:

public void Bar<T>(T obj) where T : A
{       
    obj.Foo();
    obj.Foo();
    obj.Foo();
}

为了使这个工作与转换(即:允许调用Bar(new B())) -你必须在该方法内部构造一个NEW对象实例,因为Foo没有在b上定义。这将是非常意外的,并且可能导致一些非常难以发现的bug。在上面的例子中,转换操作应该发生在每个方法调用上吗?它应该只发生一次,然后编译器做一些欺骗来使它工作吗?虽然可以想象处理这种情况的方法,但没有一种方法是明确的…

其他人已经介绍过了,但我想我还是粘贴一些特殊的东西

被认为是有效的完整列表在c#语言规范的第6.1.6章中。关键部分是最后一段:

引用转换(隐式或显式)永远不会改变被转换对象的引用标识。换句话说,虽然引用转换可能会改变引用的类型,但它不会改变永远不要改变所引用对象的类型或值。

完整的转换列表如下:

隐式引用转换为:

  • 从任何引用类型到对象和动态。
  • 从任意类类型S到任意类类型T,只要S是从T派生的
  • 从任何类类型S到任何接口类型T,只要S实现T。
  • 从任意接口类型S到任意接口类型T,只要S是从T派生的
  • 从元素类型SE的数组类型S到元素类型TE的数组类型T,前提是下列所有条件都为真:
    • S和T只是元素类型不同。也就是说,S和T具有相同的维数。
    • SE和TE都是引用类型
    • 存在从SE到TE的隐含引用转换。
  • 从任何数组类型到System。数组和它实现的接口。
  • 从单维数组类型S[]到System.Collections.Generic.IList及其基接口,提供有一个隐含的恒等或引用转换从S到t .
  • 从任何委托类型到System。委托及其实现的接口。
  • 从空文字到任何引用类型。
  • 从任何引用类型到引用类型T,如果它具有隐式标识或到引用类型T0和的引用转换T0的身份转换为t。
  • 从任何引用类型到接口或委托类型T,如果它具有隐式标识或到接口或的引用转换委托类型T0和T0是方差可转换的(§13.1.3.2)到t。
  • 涉及已知为引用类型的类型参数的隐式转换。参见§6.1.10了解更多关于隐式的细节

在您的示例代码中,B不是从A派生的。试着

class B : A
{ 
    public static implicit operator A(B me) 
    { 
        return new A(); 
    } 
} 

不,这不是一条奇怪的信息。"隐式引用转换"(规范第6.1.6节)与"用户定义的隐式转换"(第6.1.10节)不同,后者是你所拥有的。

引用转换意味着可以将给定对象的引用从一种类型转换为另一种类型("引用转换…永远不要改变被转换对象的引用标识")。

用户定义的隐式转换可以返回一个新的、不同的对象。