[解決済み] null可能なbooleanに短絡演算子||や&&は存在するのか?RuntimeBinderはそう考えることがある
質問
のC#言語仕様書を読みました。
条件付き論理演算子
||
と
&&
は、短絡的な論理演算子としても知られています。私には、これらが nullable boolean のために存在するかどうかが不明確に思えました。
Nullable<bool>
(また
bool?
と書くこともできる)ので、非ダイナミックタイピングで試してみました。
bool a = true;
bool? b = null;
bool? xxxx = b || a; // compile-time error, || can't be applied to these types
これで疑問は解決したようです(仕様を明確に理解できなかったのですが、Visual C#コンパイラの実装が正しいと仮定すれば、これでわかりました)。
しかし、私は
dynamic
バインディングも試してみたかったのです。そこで、代わりにこれを試してみました。
static class Program
{
static dynamic A
{
get
{
Console.WriteLine("'A' evaluated");
return true;
}
}
static dynamic B
{
get
{
Console.WriteLine("'B' evaluated");
return null;
}
}
static void Main()
{
dynamic x = A | B;
Console.WriteLine((object)x);
dynamic y = A & B;
Console.WriteLine((object)y);
dynamic xx = A || B;
Console.WriteLine((object)xx);
dynamic yy = A && B;
Console.WriteLine((object)yy);
}
}
驚くべき結果は、これが例外なく実行されることです。
さて
x
と
y
は驚くことではなく、それらの宣言によって両方のプロパティが取得され、結果の値も予想通りです。
x
は
true
であり
y
は
null
.
しかし
xx
の
A || B
はバインディングタイム例外を発生させず、唯一のプロパティである
A
が読み込まれただけで
B
. なぜこのようなことが起こるのでしょうか?お分かりのように、私たちは
B
ゲッターをクレイジーなオブジェクトを返すように変更することができます。
"Hello world"
のような、そして
xx
はまだ
true
と評価され、バインディングの問題は発生しません。
評価する
A && B
は
yy
) もバインディングタイムエラーを引き起こさない。そして、ここではもちろん両方のプロパティが取得されます。なぜこのようなことがランタイムバインダで許されるのでしょうか?もし
B
が "悪い" オブジェクトに変更された場合 (例えば
string
のような)悪いオブジェクトに変更された場合、バインディング例外が発生します。
これは正しい動作でしょうか? (どうして仕様から推測できるのでしょうか?)
もし、あなたが
B
を最初のオペランドとすると、両方とも
B || A
と
B && A
は実行時バインダー例外を与える (
B | A
と
B & A
は、短絡しない演算子ですべてが正常であるため、うまく動作します。
|
と
&
).
(Visual Studio 2013 の C# コンパイラ、ランタイムバージョン .NET 4.5.2 で試しました。)
どのように解決するのですか?
まず最初に、非動的な nullable-bool のケースについて仕様が明確でないことを指摘していただきありがとうございます。将来のバージョンで修正するつもりです。コンパイラーの動作は、意図された動作です。
&&
と
||
は nullable bools では動作しないことになっています。
しかし、ダイナミックバインダーはこの制限を実装していないようです。その代わり、コンポーネント操作を別々にバインドします。
&
/
|
であり
?:
. このため、最初のオペランドがたまたま
true
または
false
(の最初のオペランドとして許可されます。
?:
を使うことができます)、しかしもし
null
を最初のオペランドとして与えると (例えば
B && A
を試すと)、実行時バインディング例外が発生します。
考えてみれば、なぜ私たちが動的な
&&
と
||
このように、一つの大きな動的操作としてではなく、動的操作は実行時に結合されます。
オペランドが評価された後
そのため、結合はこれらの評価結果の実行時の型に基づくことができます。しかし、このような熱心な評価は、演算子を短絡させるという目的を破っています! そのため,代わりに生成される動的な
&&
と
||
は評価をバラバラに分割し、以下のように進めていきます。
-
左のオペランドを評価する(この結果を
x
) -
に変えてみてください。
bool
に暗黙のうちに変換するか、あるいはtrue
またはfalse
演算子 (できない場合は失敗) -
使用方法
x
の中の条件として?:
操作 -
trueブランチでは
x
を結果的に -
falseブランチで
では
は2番目のオペランドを評価します(その結果を
y
) -
をバインドしてみてください。
&
または|
演算子の実行時型に基づきx
とy
(できない場合は失敗) - 選択した演算子を適用します
これは、ある種の "illegal" なオペランドの組み合わせを通過させる動作です。
?:
演算子は、最初のオペランドをうまく
NULL でない
ブール値として扱われます。
&
または
|
演算子でうまく処理されると
nullable
ブール値として扱うことに成功し、2つの演算子が一致するかどうかを確認するために調整することはありません。
つまり、動的な && と || が nullable で動作するわけではありません。ただ、静的な場合と比較して、少し甘すぎる方法で実装されているだけです。これはおそらくバグとみなされるべきですが、私たちは決してこれを修正しないでしょう。また、動作を厳しくすることはほとんど誰の役にも立たないでしょう。
何が起こり、なぜそうなるのか、これで説明できたと思います! これは興味深い領域で、dynamic を実装したときに行った決定の結果について、私自身がしばしば困惑していることに気づきます。この質問はおいしかったですね、提起してくれてありがとう!
マッズ
関連
-
[解決済み】非静的メソッドはターゲットを必要とする
-
[解決済み】取り消せないメンバはメソッドのように使えない?
-
[解決済み】Visual studio 2019がデバッグ時にフリーズする件
-
[解決済み】HRESULTからの例外:0x800A03ECエラー
-
[解決済み】エラー「必要なフォーマルパラメータに対応する引数が与えられていない」を解決する?
-
[解決済み】インデックスが範囲外でした。コレクションパラメータname:indexのサイズより小さく、非負でなければなりません。
-
[解決済み】ユーザー設定値を別のユーザー設定値で設定する
-
[解決済み] NULL-COALESCING 演算子のカスタム暗黙変換の不思議な挙動
-
[解決済み] Stringを同等のLINQ Expression Treeに変換する方法は?
-
[解決済み] 論理演算子の短絡は必須ですか?また、評価順序は?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】プログラム実行中に1秒待つ
-
[解決済み】C#におけるtypedefの等価性
-
[解決済み] DBNullから他の型にオブジェクトをキャストすることができない
-
[解決済み] [Solved] アセンブリ System.Web.Extensions dll はどこにありますか?
-
[解決済み] 'IEnumerable<SelectListItem>' 型の ViewData アイテムで、キーが国であるものは存在しない。
-
[解決済み】Swashbuckle/Swagger + ASP.Net Core: "Failed to load API definition" (API定義の読み込みに失敗しました
-
[解決済み】OnCollisionEnter2Dが実行されない?
-
[解決済み】2年前のMSDateを把握する【クローズド
-
[解決済み] [Solved] .NETでスレッドの終了を待つには?
-
[解決済み】プロセスが実行されているかどうかを知るには?