15

このスニペットはLINQPadでコンパイルされていません。

void Main()
{
    (new[]{0,1,2,3}).Where(IsNull).Dump();
}

static bool IsNull(object arg) { return arg == null; }

コンパイラのエラーメッセージは次のとおりです。

'UserQuery.IsNull(object)'のオーバーロードはデリゲート'System.Func'と一致しません

文字列配列では機能しますが、では機能しませんint[]。どうやらボクシングと関係があるようですが、詳しく知りたいです。

4

3 に答える 3

42

与えられた答え(値型に関係する差異はない)は正しいです。可変型引数の1つが値型である場合に共変性と反変性が機能しない理由は次のとおりです。それが機能し、物事がどのようにひどくうまくいかないかを示したとしましょう:

Func<int> f1 = ()=>123;
Func<object> f2 = f1; // Suppose this were legal.
object ob = f2();

OK、どうなりますか?f2はf1と参照同一です。したがって、f1が何をするにしても、f2は行います。f1は何をしますか?32ビット整数をスタックに配置します。割り当ては何をしますか?スタック上にあるものはすべて取得し、変数「ob」に格納します。

ボクシングの指導はどこにありましたか?ありませんでした!整数ではなく、ボックス化された整数を含むヒープ位置への64ビットポインターを期待していた32ビット整数をストレージに格納しました。したがって、スタックの位置がずれているだけでなく、無効な参照で変数の内容が破損しています。すぐにプロセスは炎上します。

それで、ボクシングの指示はどこに行くべきですか?コンパイラはどこかにボクシング命令を生成する必要があります。コンパイラは、f2がすでにボックス化されているオブジェクトを返すと信じているため、f2の呼び出しの後に進むことはできません。f1はボックス化されたintではなくintを返すため、f1の呼び出しには参加できません。同じデリゲートであるため、f2の呼び出しとf1の呼び出しの間を行き来することはできません。'between'はありません

ここでできることは、2行目を実際に意味するようにすることだけです。

Func<object> f2 = ()=>(object)f1();

そして今、f1とf2の間に参照IDがないので、分散のポイントは何ですか?共変参照変換を使用することの全体的なポイントは、参照IDを保持することです。

どのようにスライスしても、物事はひどく悪くなり、それを修正する方法はありません。したがって、最善の方法は、そもそも機能を違法にすることです。値型が変化するものであるジェネリックデリゲート型では、差異は許可されません。

更新:ここで、VBでは、intを返すデリゲートをオブジェクトを返すデリゲートに変換できることに注意してください。VBは、最初のデリゲートへの呼び出しをラップし、結果をボックス化する2番目のデリゲートを生成するだけです。VBは、参照変換がオブジェクトIDを保持するという制限を放棄することを選択します。

これは、C#とVBの設計哲学の興味深い違いを示しています。C#では、設計チームは常に「コンパイラーがユーザーのプログラムのバグである可能性が高いものを見つけて、それをユーザーの注意を引くにはどうすればよいか」を常に考えています。そして、VBチームは、「ユーザーが何を意味する可能性が高いかをどのように把握し、ユーザーに代わってそれを実行できるか」を考えています。つまり、C#の哲学は「何かを見たら、何かを言う」であり、VBの哲学は「私が言うことではなく、私が言うことをする」ことです。どちらも完全に合理的な哲学です。ほぼ同一の機能セットを持つ2つの言語が、設計原則のためにこれらの細部でどのように異なるかを見るのは興味深いことです。

于 2010-11-04T15:45:45.537 に答える
3

Int32は値型であり、逆分散は値型では機能しないためです。

あなたはこれを試すことができます:

(new **object**[]{0,1,2,3}).Where(IsNull).Dump();
于 2010-11-04T12:08:39.093 に答える
-1

オブジェクトがないため、intでは機能しません。

試す:

void Fun()
{
    IEnumerable<object> objects = (new object[] { 0, 1, null, 3 }).Where(IsNull);

    foreach (object item in objects)
    {
        Console.WriteLine("item is null");
    }
}

bool IsNull(object arg) { return arg == null; }
于 2010-11-04T12:05:25.723 に答える