[英]Why does the C# compiler allow a cast to be performed with Linq but not with parentheses?
I have a generic class NamedValue<TValue>
:我有一个通用的 class
NamedValue<TValue>
:
public class NamedValue<TValue>
{
public string Name { get; set; }
public TValue Value { get; set; }
}
I have a second generic class, NamedValueSource<TValue>
that contains a List<NamedValue<TValue>>
:我有第二个通用 class,
NamedValueSource<TValue>
包含一个List<NamedValue<TValue>>
:
public class NamedValueSource<TValue>
{
public List<NamedValue<TValue>> NamedValues { get; set; }
public NamedValueSource()
{
NamedValues = GetNamedValues().Cast<NamedValue<TValue>>().ToList();
}
private IEnumerable<NamedValue<bool>> GetNamedValues()
{
var yesNamedValue = new NamedValue<bool> { Name = "Yes", Value = true };
var noNamedValue = new NamedValue<bool> { Name = "Yes", Value = false };
yield return yesNamedValue;
yield return noNamedValue;
}
}
The following test code works perfectly (the assertion passes):以下测试代码完美运行(断言通过):
public class Tester
{
public Tester()
{
var source = new NamedValueSource<bool>();
Debug.Assert(source.NamedValues[0].Name == "Yes");
}
}
Now, here's the interesting part.现在,这是有趣的部分。 If I attempt to perform the cast within
GetNamedValues()
, the code won't compile:如果我尝试在
GetNamedValues()
中执行转换,代码将无法编译:
public class NamedValueSourceFail<TValue>
{
public List<NamedValue<TValue>> NamedValues { get; set; }
public NamedValueSourceFail()
{
NamedValues = GetNamedValues().ToList();
}
private IEnumerable<NamedValue<TValue>> GetNamedValues()
{
var yesNamedValue = new NamedValue<bool> { Name = "Yes", Value = true };
var noNamedValue = new NamedValue<bool> { Name = "Yes", Value = false };
yield return (NamedValue<TValue>)yesNamedValue; // ERROR: cannot convert type
yield return (NamedValue<TValue>)noNamedValue; // ERROR: cannot convert type
}
}
Why does NamedValueSource<TValue>
compile while NamedValueSourceFail<TValue>
errors out?为什么
NamedValueSource<TValue>
编译时NamedValueSourceFail<TValue>
出错? Specifically, why am I able to perform the cast using Linq but not with good ol' parantheses?具体来说,为什么我可以使用 Linq 执行转换但不能使用好的 ol'parantheses?
Edit编辑
In case it's not entirely clear from the comment thread of the accepted answer, I simply needed to convert to object
first, then I would be allowed to cast to NamedValue<TValue>
.如果从已接受答案的评论线程中看不清楚,我只需要先转换为
object
,然后我就可以转换为NamedValue<TValue>
。 This is probably how the Linq Cast
method works behind the scenes.这可能就是 Linq
Cast
方法在幕后的工作方式。
UPDATE : This question was the subject of my blog on July 10th 2012 ;更新:这个问题是我 2012 年 7 月 10 日博客的主题; thanks for the great question!
谢谢你提出的好问题!
Let's greatly simplify your complicated program.让我们大大简化您的复杂程序。
public static class X
{
public static V Cast<V>(object o) { return (V)o; }
}
class C<T> {}
class D<U>
{
public C<U> value;
public D()
{
this.value = X.Cast<C<U>>(new C<bool>());
}
}
Now your second version, simplified:现在你的第二个版本,简化了:
class C<T> {}
class D<U>
{
public C<U> value;
public D()
{
this.value = (C<U>)(new C<bool>());
}
}
OK, so now let's ask some questions.好的,现在让我们问一些问题。
Why does the second program fail at compile time?
为什么第二个程序在编译时失败?
Because there is no conversion from C<bool>
to C<U>
for arbitrary U
.因为对于任意
U
没有从C<bool>
到C<U>
的转换。 The compiler knows that the only way this could possibly succeed is is U
is always bool, and therefore this program is almost certainly wrong!编译器知道这可能成功的唯一方法是
U
始终是 bool,因此这个程序几乎肯定是错误的! The compiler assumes that U
is going to be something other than bool some of the time.编译器假设
U
在某些时候会是除 bool 之外的其他东西。
Why then does the first program succeed at compile time?
那么为什么第一个程序在编译时成功呢?
The compiler has no idea that a method named "X.Cast" should be treated like a cast operator for the purposes of error detection, As far as the compiler is concerned, the Cast
method is a method that takes an object in and returns a V
for whatever type parameter is provided for V
.编译器不知道出于错误检测的目的,应将名为“X.Cast”的方法视为强制转换运算符,就编译器而言,
Cast
方法是一种接受 object 并返回一个V
为V
提供的任何类型参数。 When compiling the body of the ctor of D, the compiler has no idea whatsoever that some method, which probably isn't even in this program to begin with, is going to try to do a cast that is going to fail unless U
happens to be bool.在编译 D 的 ctor 的主体时,编译器根本不知道某些方法(可能甚至不在该程序中开始)将尝试执行一个将失败的转换,除非
U
碰巧布尔值。
The compiler simply has no basis upon which to treat the first version as an error, even though it most certainly is a deeply wrong program.编译器根本没有将第一个版本视为错误的依据,即使它肯定是一个严重错误的程序。 You'll have to wait until runtime to find out that your program is wrong.
您必须等到运行时才能发现您的程序是错误的。
Now let's make a third version of your program:现在让我们制作程序的第三个版本:
class C<T> {}
class D<U>
{
public C<U> value;
public D()
{
this.value = (C<U>)(object)(new C<bool>());
}
}
This succeeds at compile time, so let's ask:这在编译时成功了,所以让我们问:
Why does this succeed at compile time?
为什么这会在编译时成功?
For the exact same reason that the first one succeeds at compile time.出于与第一个在编译时成功的完全相同的原因。 When you inserted the cast you effectively said that you wanted the newly constructed
C<bool>
to be treated as an object, and so for the rest of the analysis of this expression, that expression is considered to be of type object, and not the more specific type C<bool>
.当您插入转换时,您有效地表示您希望将新构造的
C<bool>
视为 object,因此对于此表达式分析的 rest,该表达式被视为类型 object,而不是更具体的类型C<bool>
。
So then why is it legal to cast object to
C<U>
in this case?那么为什么在这种情况下将 object 转换为
C<U>
是合法的呢? Or for that matter, toV
in the first case?或者就此而言,在第一种情况下对
V
?
It is legal to cast object to V
because V
could be the type of the object, a base type of the object or an interface implemented by the object, so the compiler allows the conversion because it figures there are a lot of ways it could possibly succeed.将 object 转换为
V
是合法的,因为V
可能是 object 的类型、object 的基类型或 object 实现的接口,因此编译器允许转换,因为它认为有很多方法可能成功。
Basically, it is legal to cast object
to anything that you could convert to object
.基本上,将 object转换为可以转换为
object
的任何内容object
合法的。 You cannot cast object
to a pointer type, for instance, because no pointer type can be cast to object
.例如,您不能将
object
转换为指针类型,因为没有指针类型可以转换为object
。 But everything else is fair game.但其他一切都是公平的游戏。
By making the cast to object
first, you are removing information from the purview of the compiler;通过首先转换为
object
,您正在从编译器的权限中删除信息; you are saying "ignore the fact that you know this is always C<bool>
for the purposes of error detection.你是说“忽略你知道这总是
C<bool>
用于错误检测的事实。
In your second example, you're trying to convert NamedValue<bool>
to NamedValue<TValue>
-- this won't work, because the conversion has to be valid for any type argument.在您的第二个示例中,您试图将
NamedValue<bool>
转换为NamedValue<TValue>
——这将不起作用,因为转换必须对任何类型参数都有效。 You can't convert NamedValue<bool>
to NamedValue<int>
or NamedValue<string>
or NamedValue<AnythingElseOtherThanBool>
.您不能将
NamedValue<bool>
转换为NamedValue<int>
或NamedValue<string>
或NamedValue<AnythingElseOtherThanBool>
。
One solution is to make NamedValueSource<TValue>
abstract, as well as its GetNamedValues()
method, and then create a class BooleanNamedValueSource: NamedValueSource<bool>
class to use in your test.一种解决方案是将
NamedValueSource<TValue>
及其GetNamedValues()
方法抽象化,然后创建一个 class BooleanNamedValueSource: NamedValueSource<bool>
class 以在您的测试中使用。
In the linq case, the cast is not being done by the compiler;在 linq 的情况下,转换不是由编译器完成的; the cast occurs in a method that has already been compiled.
转换发生在已经编译的方法中。 All the compiler knows is that it is calling a method that takes
IEnumerable<bool>
and returns IEnumerable<TValue>
.编译器只知道它正在调用一个接受
IEnumerable<bool>
并返回IEnumerable<TValue>
的方法。 The specifics of that conversion are entirely invisible to the compiler.该转换的细节对编译器来说是完全不可见的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.