简体   繁体   English

为什么 C# 编译器允许使用 Linq 而不是使用括号执行强制转换?

[英]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 并返回一个VV提供的任何类型参数。 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, to V 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.

相关问题 为什么C#编译器允许在IEnumerable <T>和TAlmostAnything之间进行显式转换? - Why does the C# compiler allow an explicit cast between IEnumerable<T> and TAlmostAnything? 为什么C#不允许匿名转换为对象? - Why does C# not allow anonymous cast to objects? 为什么 C# 编译器允许嵌套范围内的重复变量? - Why does the C# compiler allow a duplicated variable in nested scope? 为什么C#编译器允许空枚举? - Why does the C# compiler allow empty enums? 为什么C#编译器会对这个嵌套的LINQ查询感到厌烦? - Why does the C# compiler go mad on this nested LINQ query? 为什么编译器允许我在C#中将null转换为特定类型? - Why does the compiler let me cast a null to a specific type in C#? 如果调用ToList或ToArray,为什么C#编译器允许在表达式树中使用动态操作 - Why does C# compiler allow use of a dynamic operation in expression tree if ToList or ToArray is called 为什么c#编译器允许返回值类型和变量类型的错误匹配? - Why does c# compiler allow incorrect match of return value type and variable type? 为什么零长度堆栈分配使 C# 编译器乐于允许条件堆栈分配? - Why does a zero-length stackalloc make the C# compiler happy to allow conditional stackallocs? C#编译器如何处理重载显式转换运算符? - How does the C# compiler handle overloading explicit cast operators?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM