你能用隐式转换满足泛型约束吗?

时间:2011-11-17 18:07:56

标签: c# generics implicit-conversion

鉴于以下类型:

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的隐式引用转换。这只是一条奇怪的消息吗? 是隐式引用转换正如亚当罗宾逊的答案所示。消息是正确的。

请注意MSDN says:

  

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

这解释了为什么不允许这样做,因为B不是来自A

4 个答案:

答案 0 :(得分:10)

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

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

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

引用转换将是引用本身不会更改的位置,这意味着有问题的类型必须继承(对于类)或实现(对于接口)所讨论的类型。如果我这样做:

class A { }
class B : A { }

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

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

同样,引用是相同的,但演员是明确的。

因此,简而言之,MSDN文档更清晰但不太精确。

答案 1 :(得分:7)

这是不可能的。

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

这很有道理 - 想想编译器在下面会做什么:

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())) - 你必须在该方法内构造一个新的对象实例,因为Foo没有在B上定义。这将是非常意外,并可能导致一些非常难以发现的错误。在上面,每次方法调用都应该进行转换操作吗?它应该发生一次,并且编译器做一些诡计让它工作?虽然可以想象如何处理这个问题,但没有一种方法是明确的......

答案 2 :(得分:1)

其他人已经覆盖了它,但我认为我会粘贴一些规范

认为有效的完整列表在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.Array及其实现的接口。
  •   
  • 从一维数组类型S []到System.Collections.Generic.IList及其基接口,提供   从S到S的隐式标识或引用转换   吨。
  •   
  • 从任何委托类型到System.Delegate及其实现的接口。
  •   
  • 从null文字到任何引用类型。
  •   
  • 从任何引用类型到引用类型T,如果它具有隐式标识或引用转换为引用类型T0和   T0具有转换为T的身份。
  •   
  • 从任何引用类型到接口或委托类型T,如果它具有到接口的隐式标识或引用转换或   委托类型T0和T0是方差可转换(第13.1.3.2节)到T。
  •   
  • 涉及已知为引用类型的类型参数的隐式转换。有关隐式的更多详细信息,请参见第6.1.10节   涉及类型参数的转换。
  •   

答案 3 :(得分:-1)

在您的示例代码中,B不是来自A。尝试

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

不,这不是一个奇怪的消息。 “隐式引用转换”(规范部分6.1.6。)与“用户定义的隐式转换”(第6.1.10节)不同,这就是您所拥有的。

引用转换意味着您可以将对给定对象的引用从一种类型转换为另一种类型(“引用转换...永远不会更改正在转换的对象的引用标识”。)

用户定义的隐式转换可以(就像你的那样)返回一个新的不同对象。