Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/laravel/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 您能否通过隐式转换满足泛型约束?_C#_Generics_Implicit Conversion - Fatal编程技术网

C# 您能否通过隐式转换满足泛型约束?

C# 您能否通过隐式转换满足泛型约束?,c#,generics,implicit-conversion,C#,Generics,Implicit Conversion,鉴于这些类型: class A { } class B { public static implicit operator A(B me) { return new A(); } } class Test<T> where T : A { } class A{} B类 { 公共静态隐式运算符A(B me) { 返回新的A(); } } 类测试,其中T:A{} 我试过了 var b = new Test<B>(); var b

鉴于这些类型:

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

class Test<T> where T : A { }
class A{}
B类
{
公共静态隐式运算符A(B me)
{
返回新的A();
}
}
类测试,其中T:A{}
我试过了

var b = new Test<B>();
var b=新测试();
并期望它失败,但它确实失败了。但是错误消息是

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

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

注意

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

这就解释了为什么它是不允许的,因为
B
不是从
A

派生出来的,这是不可能的

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

这是非常有意义的-想想编译器在以下方面会做什么:

class A 
{
    public void Foo();
}
class B
{
    public static implicit operator A(B me)
    {
        return new A();
    }
}
B foo = new B();
A bar = foo;
现在,假设你有:

public void Bar<T>(T obj) where T : A
{       
    obj.Foo();
    obj.Foo();
    obj.Foo();
}
public void Bar(T obj),其中T:A
{       
obj.Foo();
obj.Foo();
obj.Foo();
}
为了使转换(即:允许调用
Bar(new B())
)-您必须在该方法内部构造一个新的对象实例,因为Foo没有在B上定义。这将是非常意外的,并且可能导致一些非常难以发现的bug。在上面的例子中,转换操作是否应该发生在每个方法调用上?它是否应该发生一次,然后编译器通过一些技巧使其工作?虽然可以想象处理这个问题的方法,但没有一种方法是明确的…

在示例代码中,
B
不是从
A
派生出来的。试一试

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

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

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


用户定义的隐式转换可以(与您的一样)返回新的、不同的对象。

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

class A 
{
    public void Foo();
}
class B
{
    public static implicit operator A(B me)
    {
        return new A();
    }
}
B foo = new B();
A bar = foo;
但是,请注意,
foo
bar
现在包含不同的引用。隐式类型转换创建了
a
的一个新实例,该实例(按照约定)在逻辑上应与
foo
等效。但问题是,这是一个不同的参考

引用转换是指引用本身不发生更改的情况,这意味着所讨论的类型必须继承(对于类)所讨论的类型,或者实现(对于接口)所讨论的类型。如果我这样做:

class A { }
class B : A { }
那么我上面的代码现在将在
foo
bar
中保存相同的引用。这就是隐式引用转换的含义。相反,显式引用转换将是向下转换,如下所示:

A foo = new B();
B bar = (B)foo;
同样,引用是相同的,但强制转换是显式的


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

其他人主要介绍了它,但我想我应该粘贴一些规范内容

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到的隐式标识或引用转换 T
  • 从任何委托类型到System.delegate及其实现的接口
  • 从空文本到任何引用类型
  • 从任何引用类型到引用类型T,如果它具有隐式标识或到引用类型T0的引用转换,并且 T0具有到T的标识转换
  • 从任何引用类型到接口或委托类型T(如果它具有到接口或委托类型的隐式标识或引用转换) 代表类型T0和T0的差异可转换为T(§13.1.3.2)
  • 包含已知为引用类型的类型参数的隐式转换。参见§6.1.10了解隐式 涉及类型参数的转换
呃…那还是不行(OP是