簡體   English   中英

為什么 C# 編譯器允許使用 Linq 而不是使用括號執行強制轉換?

[英]Why does the C# compiler allow a cast to be performed with Linq but not with parentheses?

我有一個通用的 class NamedValue<TValue>

public class NamedValue<TValue>
{
    public string Name { get; set; }
    public TValue Value { get; set; }
}

我有第二個通用 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;
    }
}

以下測試代碼完美運行(斷言通過):

public class Tester
{
    public Tester()
    {
        var source = new NamedValueSource<bool>();
        Debug.Assert(source.NamedValues[0].Name == "Yes");
    }
}

現在,這是有趣的部分。 如果我嘗試在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
    }
}

為什么NamedValueSource<TValue>編譯時NamedValueSourceFail<TValue>出錯? 具體來說,為什么我可以使用 Linq 執行轉換但不能使用好的 ol'parantheses?

編輯

如果從已接受答案的評論線程中看不清楚,我只需要先轉換為object ,然后我就可以轉換為NamedValue<TValue> 這可能就是 Linq Cast方法在幕后的工作方式。

更新:這個問題是我 2012 年 7 月 10 日博客的主題 謝謝你提出的好問題!


讓我們大大簡化您的復雜程序。

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>());
    }
}

現在你的第二個版本,簡化了:

class C<T> {}
class D<U>
{
    public C<U> value;
    public D()
    {
        this.value = (C<U>)(new C<bool>());
    }
}

好的,現在讓我們問一些問題。

為什么第二個程序在編譯時失敗?

因為對於任意U沒有從C<bool>C<U>的轉換。 編譯器知道這可能成功的唯一方法是U始終是 bool,因此這個程序幾乎肯定是錯誤的! 編譯器假設U在某些時候會是除 bool 之外的其他東西。

那么為什么第一個程序在編譯時成功呢?

編譯器不知道出於錯誤檢測的目的,應將名為“X.Cast”的方法視為強制轉換運算符,就編譯器而言, Cast方法是一種接受 object 並返回一個VV提供的任何類型參數。 在編譯 D 的 ctor 的主體時,編譯器根本不知道某些方法(可能甚至不在該程序中開始)將嘗試執行一個將失敗的轉換,除非U碰巧布爾值。

編譯器根本沒有將第一個版本視為錯誤的依據,即使它肯定是一個嚴重錯誤的程序。 您必須等到運行時才能發現您的程序是錯誤的。

現在讓我們制作程序的第三個版本:

class C<T> {}
class D<U>
{
    public C<U> value;
    public D()
    {
        this.value = (C<U>)(object)(new C<bool>());
    }
}

這在編譯時成功了,所以讓我們問:

為什么這會在編譯時成功?

出於與第一個在編譯時成功的完全相同的原因。 當您插入轉換時,您有效地表示您希望將新構造的C<bool>視為 object,因此對於此表達式分析的 rest,該表達式被視為類型 object,而不是更具體的類型C<bool>

那么為什么在這種情況下將 object 轉換為C<U>是合法的呢? 或者就此而言,在第一種情況下對V

將 object 轉換為V是合法的,因為V可能是 object 的類型、object 的基類型或 object 實現的接口,因此編譯器允許轉換,因為它認為有很多方法可能成功。

基本上,將 object轉換為可以轉換為object的任何內容object合法的。 例如,您不能將object轉換為指針類型,因為沒有指針類型可以轉換為object 但其他一切都是公平的游戲。

通過首先轉換為object ,您正在從編譯器的權限中刪除信息 你是說“忽略你知道這總是C<bool>用於錯誤檢測的事實。

在您的第二個示例中,您試圖將NamedValue<bool>轉換為NamedValue<TValue> ——這將不起作用,因為轉換必須對任何類型參數都有效。 您不能將NamedValue<bool>轉換為NamedValue<int>NamedValue<string>NamedValue<AnythingElseOtherThanBool>

一種解決方案是將NamedValueSource<TValue>及其GetNamedValues()方法抽象化,然后創建一個 class BooleanNamedValueSource: NamedValueSource<bool> class 以在您的測試中使用。

在 linq 的情況下,轉換不是由編譯器完成的; 轉換發生在已經編譯的方法中。 編譯器只知道它正在調用一個接受IEnumerable<bool>並返回IEnumerable<TValue>的方法。 該轉換的細節對編譯器來說是完全不可見的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM